Presentation provides introduction and detailed explanation of the Java 8 Lambda and Streams. Lambda covers with Method references, default methods and Streams covers with stream operations,types of streams, collectors. Also streams are elaborated with parallel streams and benchmarking comparison of sequential and parallel streams.
Additional slides are covered with Optional, Splitators, certain projects based on lambda and streams
7. Lambda expression is like a method –params, body
Parameters – declared or inferred type
(int x) -> x +1
(x) -> x+1
Lambda body – single expression or block
Unlike anonymous class, this correspond to encl0sing class
Any local variable used in lambda body must be declared final or
effectively final
void m1(int x) { int y = 1; foo(() -> x+y); // Legal: x and y are both
effectively final. }
A local variable or a method, constructor, lambda, or exception
parameter is effectively final if it is not final but it never occurs as the
left hand operand of an assignment operator (15.26) or as the operand of
an increment or decrement operator
void m6(int x) { foo(() -> x+1); x++; // Illegal: x is not effectively final. }
8. Lambda Syntax
/* argument list */
(int x, int y) -> { return x*y; }
(x, y) -> { return x*y; }
x -> { return x*2; }
() -> { System.out.println("Do you think this will work?"); }
() -> {throw new RuntimeException();}
/* single expression */
b -> { b.getMissingPages() > threshold ? b.setCondition(BAD)
: b.setCondition(GOOD) }
/* list of statements */
b -> {
Condition c = computeCondition(b.getMissingPages());
b.setCondition(c);
}
10. Lambda expression vs Anonymous
Classes
this keyword
What they are compiled into?
11. Functional Interfaces(FI)
• Lambdas are backed by interfaces
• Single abstract methods
• Functional Interface = Interface w/ 1 Method
• Names of Interface and Method are irrelevant
• Java API defines FI in java.util.function package
@FunctionalInterface
public interface Calculator
{
int calculate(int x, int y);
}
Calculator multiply = (x, y) -> x * y;
Calculator divide = (x, y) -> x / y;
int product = multiply.calculate(10, 20);
int quotient = divide.calculate(10, 20);
someMethod(multiply, divide);
anotherMethod((x, y) -> x ^ y);
12. interface Runnable { void run(); }
// Functional
interface Foo { boolean equals(Object obj); }
// Not functional; equals is already an implicit member
interface Bar extends Foo { int compare(String o1, String o2);
}
// Functional; Bar has one abstract non-Object method
interface Comparator<T> {
boolean equals(Object obj);
int compare(T o1, T o2);
}
// Functional; Comparator has one abstract non-Object method
13. Functional Interfaces
Function <T, R>
R apply(T t);
Supplier<T
>
T get()
Functional
Interfaces
Consumer
Function
Predicate
Supplier
Consumer<T>
void accept(T t);
Predicate<T>
boolean test(T
t);
14. Some usages of FI in JavaAPI
Consumer
Iterable.forEach(Consumer<? super T> action)
Supplier
ThreadLocal(Supplier<T> supplier)
Predicate
Conditions like AND, OR, NEGATE, TEST…
ArrayList.removeIf(Predicate<? super E> filter)
public static void filter(List<?> names, Predicate<Object> condition)
{ names.stream().filter((name) ->
(condition.test(name))).forEach((name) -> { System.out.println(name +
" "); }); }
Function
Comparator
Collections.sort(empList, (Employee e1, Employee e2) ->
e1.id.compareTo(e2.id));
18. Default methods
Default methods enable new functionality to be
added to the interfaces of libraries and ensure binary
compatibility with code written for older versions of
those interfaces.
@FunctionalInterface
public interface Calculator
{
int calculate(int x, int y);
default int multiply(int x, int y)
{
return x * y;
}
}
• Can be overloaded
• Can be static or instance based
• Introduce multiple inheritance
interface java.lang.Iterable<T> {
abstract Iterator<T> iterator();
default void forEach(Consumer<? super T> consumer) {
for (T t : this) {
consumer.accept(t);
}
}
}
java.lang.Iterable<Object> i = () ->
java.util.Collection.emptyList().iterator();
20. Streams
A pipes-and-filters based API for collections
This may be familiar...
ps -ef | grep java | cut -c 1-9 | sort -n | uniq
A Stream is an abstraction that represents zero or more values (not objects)
Pipelines
A stream source
Zero or more intermediate operations
a terminal operations
A pipeline can be executed in parallel
interface java.util.stream.Stream<T>
forEach()
filter()
map()
reduce()
…
java.util.Collection<T>
Stream<T> stream()
Stream<T> parallelStream()
21. Streams can be obtained in a number of ways. Some examples include:
• From a Collection via the stream() and parallelStream() methods;
• From an array via Arrays.stream(Object[]);
• From static factory methods on the stream classes, such as
Stream.of(Object[]), IntStream.range(int, int) or Stream.iterate(Object,
UnaryOperator);
• The lines of a file can be obtained from BufferedReader.lines();
• Streams of file paths can be obtained from methods in Files;
• Streams of random numbers can be obtained from Random.ints();
• Numerous other stream-bearing methods in the JDK, including
BitSet.stream(), Pattern.splitAsStream(java.lang.CharSequence), and
JarFile.stream().
22. Creating and using a Stream
List<Book> myBooks = …;
Stream<Book> books = myBooks.stream();
Stream<Book> goodBooks =
books.filter(b -> b.getStarRating() > 3);
goodBooks.forEach(b -> System.out.println(b.toString()));
23. Properties of Streams
Streams do not store elements…
…they are a view on top of a data structure
Operations provided by Streams...
…are applied to the underlying data source elements
Stream Operations can take as a parameter…
…Lambda expressions
…Method references
Manipulating the underlying data source...
…will yield a ConcurrentModificationException
24.
25. Stream Operations
builder() Returns a builder for a Stream.
filter(Predicate<? super T> predicate) Returns a stream consisting of the
elements of this stream that match the given predicate.
flatMap(Function<? super T,? extends Stream<? extends R>> mapper) Returns a
stream consisting of the results of replacing each element of this stream with
the contents of a mapped stream produced by applying the provided mapping
function to each element.
reduce(BinaryOperator<T> accumulator) Performs a reduction on the elements
of this stream, using an associative accumulation function, and returns an
Optional describing the reduced value, if any.
iterate(T seed, UnaryOperator<T> f) Returns an infinite sequential ordered
Stream produced by iterative application of a function f to an initial element
seed, producing a Stream consisting of seed, f(seed), f(f(seed)), etc.
peek(Consumer<? super T> action) Returns a stream consisting of the elements
of this stream, additionally performing the provided action on each element as
elements are consumed from the resulting stream.
Stream
operations
Build
Filter
Map
Reduce
Iterate
Peek
26.
27. Intermediate vs. Terminal
Intermediate: Output is another Stream
filter()
map()
…
Terminal: Do something else with the Stream
forEach()
reduce()
…
double totalPrice = books.mapToDouble(Book::getPrice)
.reduce(0.0, (p1, p2) -> p1+p2);
28. Stream Evaluation
Intermediate Streams are not evaluated…
…until a Terminal Operation is invoked on them
Intermediate = Lazy
Terminal = Eager (Consuming)
This allows Java to…
…do some code optimization during compilation
…avoid buffering intermediate Streams
…handle parallel Streams more easily
29.
30. Stateless Intermediate Operations
Operation need nothing other than the current Stream
element to perform its work
Examples
map() Maps element to something else
filter() Apply predicate and keep or drop element
List<Book> myBooks = ...;
double impairments = myBooks.stream()
.filter(b -> b.getCondition().equals(BAD))
.mapToDouble(Book::getPrice)
.reduce(0.0, (p1, p2) -> p1 + p2);
31. Stateful Intermediate Operations
Operations that require not only the current stream element
but also additional state
distinct() Element goes to next stage if it appears the first time
sorted() Sort elements into natural order
sorted(Comparator) Sort according to provided Comparator
substream(long) Discard elements up to provided offset
substream(long, long) Keep only elements in between offsets
limit(long) Discard any elements after the provided max. size
myBooks.stream().map(Book::getAuthor).distinct().forEach(System.out::println);
32.
33. Short-Circuiting Operations
Processing might stop before the last element of the
Stream is reached
Intermediate
limit(long)
substream(long, long)
Terminal
anyMatch(Predicate)
allMatch(Predicate)
noneMatch(Predicate)
findFirst()
findAny()
Author rp = new Author("Rosamunde Pilcher");
boolean phew = myBooks.stream()
.map(Book::getAuthor)
.noneMatch(isEqual(rp));
System.out.println("Am I safe? " + phew);
34.
35. Collectors
<R> R collect(Collector<? super T, A, R> col)
Collect the elements of a Stream into some other data
structure
Powerful and complex tool
Collector is not so easy to implement, but…
…luckily there are lots of factory methods for everyday
use in java.util.stream.Collectors
toList()
toSet()
toCollection(Supplier)
toMap(Function, Function)
…
40. Parallelization
• Must avoid side-effects and mutating
state
• Problems must fit the associativity
property
• Ex: ((a * b) * c) = (a * (b * c))
• Must be enough parallelizable code
• Performance not always better
• Can’t modify local variables (unlike for
loops)
41. Streams
Good
• Allow abstraction of details
• Communicate intent clearly
• Concise
• On-demand parallelization
Bad
• Loss of flexibility and control
• Increased code density
• Can be less efficient
• On-demand parallelization
42.
43.
44. Lambda expressions
Remove the Permanent Generation
Small VM
Parallel Array Sorting
Bulk Data Operations for Collections
Define a standard API for Base64 encoding and
decoding
New Date & Time API
Provide stronger Password-Based-Encryption (PBE)
algorithm implementations in the SunJCE provider
45. Optional
One interesting new class, used in the Stream API, is
Optional in java.util.
It is basically an alternative to using null explicitly - it is
returned by some stream operators when it is not
certain that there is a result (e.g. when reducing).
To check whether it has any contents, isPresent can be
called. If an Option has contents, get will return it.
SoundCard soundcard = ...;
if(soundcard != null){
System.out.println(soundcard);
}
You can use the ifPresent() method, as follows:
Optional<Soundcard> soundcard = ...;
soundcard.ifPresent(System.out::println);
46. Spliterator
A spliterator is the parallel analogue of an Iterator; it
describes a (possibly infinite) collection of elements, with
support for sequentially advancing, bulk traversal, and
splitting off some portion of the input into another
spliterator which can be processed in parallel.
At the lowest level, all streams are driven by a spliterator.
To support the parallel execution of the pipeline, the data
elements in the original collection must be split over multiple
threads.
The Spliterator interface, also in java.util, provides this
functionality.
The method trySplit returns a new Spliterator that manages a
subset of the elements of the original Spliterator. The original
Spliterator then skips elements in the subset that was
delegated. An ideal Spliterator might delegate the
management of half of its elements to a new Spliterator (up
to a certain threshold), so that users can easily break down
the set of data, e.g. for parallelization purposes.
47. Joining Collector
Used for concatenation of CharSequences
Internally implemented using StringBuilder
A lot more efficient than a Map-Reduce with
intermediately concatenated Strings
// not efficient due to recursive String concatenation. And ugly.
String titleList = myBooks.stream().map(Book::getTitle).reduce("", (t1, t2) -> t1+t2);
// Still inefficient. Still ugly (initial line break)
titleList = myBooks.stream().map(Book::getTitle).reduce("", (t1, t2) -> t1+"n"+t2);
// more efficient thanks to StringBuilder. Pretty printed.
titleList = myBooks.stream().map(Book::getTitle).collect(Collectors.joining("n"));
48. Projects based on Lambda and
streams
Apache Spark
Spring-io sagan
Jlinq (http://www.jinq.org/)
49.
50. Functional Interfaces
There will be also new functional interfaces, such as
Predicate<T> and Block<T>
Default: java.util.function.Consumer<T>
public interface Stream<T> {
void forEach(Consumer<? super T> consumer);
}
public interface Consumer<T> {void accept(T t);}
Consumer<Book> reduceRankForBadAuthors =
(Book b) -> { if (b.getStarRating() < 2) b.getAuthor().addRank(-1); };
books.forEach(reduceRankForBadAuthors);
books.forEach(b -> b.setEstimatedReadingTime(90*b.getPages()));
51. Terminal = Consuming Operations
Intermediate Operations can be chained
Only one Terminal Operation can be invoked
Best avoid reference variables to Streams entirely by
using Fluent Programming
Construction (Intermediate)* Terminal;
books.forEach(b -> System.out.println("Book: " + b.getTitle()));
double totalPrice = books.reduce(0.0, (b1, b2)
-> b1.getPrice() + b2.getPrice());
Exception in thread "main" java.lang.IllegalStateException:
stream has already been operated upon or closed