JDK8: Lessons Learnt with Lambdas and Streams

841 views

Published on

SpringOne Platform 2016
Speaker: Simon Ritter; Deputy CTO, Azul Systems

Lambda expressions and the streams API add a more functional style of programming to Java; something developers have not really had in the past.

This session will start with a short summary of the key features of both Lambda expressions and streams before moving on to some real world examples of how to use them effectively, including a number of lessons learnt from trying to apply an imperative style of programming when it should have been functional.

We'll finish off with a quick look at some of the ideas for improvements to streams in JDK 9.

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
841
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
29
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

JDK8: Lessons Learnt with Lambdas and Streams

  1. 1. © Copyright Azul Systems 2016 © Copyright Azul Systems 2015 @speakjava Lessons Learnt With Lambdas and Streams in JDK 8 Simon Ritter Deputy CTO, Azul Systems 1
  2. 2. © Copyright Azul Systems 2016 A clever man learns from his mistakes... ...a wise man learns from other people’s
  3. 3. © Copyright Azul Systems 2016 Lambda Expressions And Delayed Execution
  4. 4. © Copyright Azul Systems 2016 Performance Impact For Logging  Heisenberg’s uncertainty principle  Setting log level to INFO still has a performance impact  Since Logger determines whether to log the message the parameter must be evaluated even when not used 4 logger.finest(getSomeStatusData()); Always executed
  5. 5. © Copyright Azul Systems 2016 Supplier<T>  Represents a supplier of results  All relevant logging methods now have a version that takes a Supplier  Pass a description of how to create the log message – Not the message  If the Logger doesn’t need the value it doesn’t invoke the Lambda  Can be used for other conditional activities 5 logger.finest(getSomeStatusData());logger.finest(() -> getSomeStatusData());
  6. 6. © Copyright Azul Systems 2016 Avoiding Loops In Streams
  7. 7. © Copyright Azul Systems 2016 Functional v. Imperative  For functional programming you should not modify state  Java supports closures over values, not closures over variables  But state is really useful… 7
  8. 8. © Copyright Azul Systems 2016 Counting Methods That Return Streams 8 Still Thinking Imperatively Set<String> sourceKeySet = streamReturningMethodMap.keySet(); LongAdder sourceCount = new LongAdder(); sourceKeySet.stream() .forEach(c -> sourceCount .add(streamReturningMethodMap.get(c).size()));
  9. 9. © Copyright Azul Systems 2016 Counting Methods That Return Streams 9 Functional Way sourceKeySet.stream() .mapToInt(c -> streamReturningMethodMap.get(c).size()) .sum();
  10. 10. © Copyright Azul Systems 2016 Printing And Counting 10 Still Thinking Imperatively LongAdder newMethodCount = new LongAdder(); functionalParameterMethodMap.get(c).stream() .forEach(m -> { output.println(m); if (isNewMethod(c, m)) newMethodCount.increment(); });
  11. 11. © Copyright Azul Systems 2016 Printing And Counting 11 More Functional, But Not Pure Functional int count = functionalParameterMethodMap.get(c).stream() .mapToInt(m -> { int newMethod = 0; output.println(m); if (isNewMethod(c, m)) newMethod = 1; return newMethod }) .sum(); There is still state being modified in the Lambda
  12. 12. © Copyright Azul Systems 2016 Printing And Counting 12 Even More Functional, But Still Not Pure Functional int count = functionalParameterMethodMap.get(nameOfClass) .stream() .peek(method -> output.println(method)) .mapToInt(m -> isNewMethod(nameOfClass, m) ? 1 : 0) .sum(); Strictly speaking printing is a side effect, which is not purely functional
  13. 13. © Copyright Azul Systems 2016 The Art Of Reduction (Or The Need to Think Differently)
  14. 14. © Copyright Azul Systems 2016 A Simple Problem  Find the length of the longest line in a file  Hint: BufferedReader has a new method, lines(), that returns a Stream 14 BufferedReader reader = ... int longest = reader.lines() .mapToInt(String::length) .max() .getAsInt();
  15. 15. © Copyright Azul Systems 2016 Another Simple Problem  Find the length of the longest line in a file 15
  16. 16. © Copyright Azul Systems 2016 Another Simple Problem  Find the length of the longest line in a file 16
  17. 17. © Copyright Azul Systems 2016 Naïve Stream Solution  That works, so job done, right?  Not really. Big files will take a long time and a lot of resources  Must be a better approach 17 String longest = reader.lines(). sort((x, y) -> y.length() - x.length()). findFirst(). get();
  18. 18. © Copyright Azul Systems 2016 External Iteration Solution  Simple, but inherently serial  Not thread safe due to mutable state 18 String longest = ""; while ((String s = reader.readLine()) != null) if (s.length() > longest.length()) longest = s;
  19. 19. © Copyright Azul Systems 2016 Recursive Approach 19 String findLongestString(String longest, BufferedReader reader) { String next = reader.readLine(); if (next == null) return longest; if (next.length() > longest.length()) longest = next; return findLongestString(longest, reader); }
  20. 20. © Copyright Azul Systems 2016 Recursion: Solving The Problem  No explicit loop, no mutable state, we’re all good now, right?  Unfortunately not: – larger data sets will generate an OOM exception – Too many stack frames 20 String longest = findLongestString("", reader);
  21. 21. © Copyright Azul Systems 2016 A Better Stream Solution  Stream API uses the well known filter-map-reduce pattern  For this problem we do not need to filter or map, just reduce Optional<T> reduce(BinaryOperator<T> accumulator)  BinaryOperator is a subclass of BiFunction – R apply(T t, U u)  For BinaryOperator all types are the same – T apply(T x, T y) 21
  22. 22. © Copyright Azul Systems 2016 A Better Stream Solution  The key is to find the right accumulator – The accumulator takes a partial result and the next element, and returns a new partial result – In essence it does the same as our recursive solution – But without all the stack frames 22
  23. 23. © Copyright Azul Systems 2016 A Better Stream Solution  Use the recursive approach as an accululator for a reduction 23 String longestLine = reader.lines() .reduce((x, y) -> { if (x.length() > y.length()) return x; return y; }) .get();
  24. 24. © Copyright Azul Systems 2016 A Better Stream Solution  Use the recursive approach as an accululator for a reduction 24 String longestLine = reader.lines() .reduce((x, y) -> { if (x.length() > y.length()) return x; return y; }) .get(); x in effect maintains state for us, by providing the partial result, which is the longest string found so far
  25. 25. © Copyright Azul Systems 2016 The Simplest Stream Solution  Use a specialised form of max()  One that takes a Comparator as a parameter  comparingInt() is a static method on Comparator Comparator<T> comparingInt( ToIntFunction<? extends T> keyExtractor) 25 reader.lines() .max(comparingInt(String::length)) .get();
  26. 26. © Copyright Azul Systems 2016 Conclusions
  27. 27. © Copyright Azul Systems 2016 Conclusions  Lambdas and Stream are a very powerful combination  Does require developers to think differently – Avoid loops, even non-obvious ones! – Reductions  Be careful with parallel streams  More to come in JDK 9 (and 10)  Join the Zulu.org community – www.zulu.org 27
  28. 28. © Copyright Azul Systems 2016 © Copyright Azul Systems 2015 @speakjava Q & A Simon Ritter Deputy CTO, Azul Systems 28

×