Java 8 Lambda and Streams

1,607 views
1,430 views

Published on

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

Published in: Software, Technology, Education
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,607
On SlideShare
0
From Embeds
0
Number of Embeds
14
Actions
Shares
0
Downloads
0
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Java 8 Lambda and Streams

  1. 1. Agenda • Java 8 • Lambdas • Method References • Default Methods Lambda • Stream Operations • Intermediate vs. Terminal • Stateless vs. Stateful • Short-Circuiting • Collectors • Parallel Streams • Benchmark Sequential and Paralllel stream Stream
  2. 2. Java-style functional programming CollectionsStreams Functional interfaces Functional Java
  3. 3. Java™ SE 8 Release Contents  JSR 335: Lambda Expressions closures  JEP 107: Bulk Data Operations for Collections for-each filter map reduce http://www.jcp.org/en/jsr/detail?id=337 http://openjdk.java.net/jeps/107
  4. 4. Object Oriented ReflectiveStructured Functional Generic Concurrent GenericImperative “…Is a blend of imperative and object oriented programming enhanced with functional flavors”
  5. 5.  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. }
  6. 6. 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); }
  7. 7. FewcommonusagesofLambdaexpression Anonymous Class Event Handling Iterate over List Parallel processing of Collection elements at API level Functional Programming Streams(Collection) - Map , Reduce, Filter …
  8. 8. Lambda expression vs Anonymous Classes  this keyword  What they are compiled into?
  9. 9. 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);
  10. 10.  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
  11. 11. 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);
  12. 12. 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));
  13. 13. Method References books.forEach(b -> b.fixSpellingErrors()); books.forEach(Book::fixSpellingErrors); // instance method books.forEach(b -> BookStore.generateISBN(b)); books.forEach(BookStore::generateISBN); // static method books.forEach(b -> System.out.println(b.toString())); books.forEach(System.out::println); // expression Stream<ISBN> isbns1 = books.map(b -> new ISBN(b)); Stream<ISBN> isbns2 = books.map(ISBN::new); // constructor
  14. 14. 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();
  15. 15. Operation 1 Operation 2 Operation 3 Operation 4 Stream Lambda Lambda Lambda Lambda
  16. 16. 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()
  17. 17. 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().
  18. 18. 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()));
  19. 19. 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
  20. 20. 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
  21. 21. 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);
  22. 22. 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
  23. 23. 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);
  24. 24. 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);
  25. 25. 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);
  26. 26. 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) …
  27. 27. Collector Examples List<Author> authors = myBooks.stream() .map(Book::getAuthor) .collect(Collectors.toList()); double averagePages = myBooks.stream() .collect(Collectors.averagingInt(Book::getPages));
  28. 28. Parallel Streams • Uses fork-join used under the hood • Thread pool sized to # cores • Order can be changed
  29. 29. Parallel Streams Imperative Serial Stream Parallel Stream 8,128 0 1 0 33,550,336 190 229 66 8,589,869,056 48648 59646 13383 137,438,691,328 778853 998776 203651 private static boolean isPerfect(long n) { return n > 0 && LongStream.rangeClosed(1, n / 2). parallel(). filter(i -> n % i == 0). reduce(0, (l, r) -> l + r) == n; } List<Long> perfectNumbers = LongStream.rangeClosed(1, 8192).parallel(). filter(PerfectNumberFinder::isPerfect). collect(ArrayList<Long>::new, ArrayList<Long>::add, ArrayList<Long>::addAll);
  30. 30. 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)
  31. 31. 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
  32. 32.  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
  33. 33. 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);
  34. 34. 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.
  35. 35. 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"));
  36. 36. Projects based on Lambda and streams  Apache Spark  Spring-io sagan  Jlinq (http://www.jinq.org/)
  37. 37. 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()));
  38. 38. 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

×