Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Streams: The Good, The Bad And The Ugly

349 views

Published on

Materials from a BoF held with Stuart Marks discussing lessons learnt about how to use Streams in JDK 8 and examples of how not to use them.

Published in: Software
  • Be the first to comment

Streams: The Good, The Bad And The Ugly

  1. 1. © Copyright Azul Systems 2016 © Copyright Azul Systems 2015 @speakjava Streams in JDK 8: The Good, The Bad & The Ugly Simon Ritter Deputy CTO, Azul Systems 1
  2. 2. © Copyright Azul Systems 2016 Goals Of This BoF  Look at Streams code  Discuss whether it’s good, bad or ugly  Learn some things to do  And some things not to do  Idea came about because someone didn’t like my code 2
  3. 3. © Copyright Azul Systems 2016 Problem To Solve  Code to extract key-value pairs from HTTP parameters  Results: – action, validate – key, [10, 22, 10, 33] 3 action=validate&key=10&key=22&key=10&key=33
  4. 4. © Copyright Azul Systems 2016 Non-Stream Code private void parseQuery(String query, Map parameters) { if (query != null) { String pairs[] = query.split("[&]"); for (String pair : pairs) { String param[] = pair.split("[=]"); String key = null; String value = null; if (param.length > 0) key = URLDecoder.decode(param[0], ENC); if (param.length > 1) value = URLDecoder.decode(param[1], ENC);
  5. 5. © Copyright Azul Systems 2016 Non-Stream Code if (parameters.containsKey(key)) { Object obj = parameters.get(key); if (obj instanceof List) { List values = (List)obj; values.add(value); } else if(obj instanceof String) { List values = new ArrayList(); values.add((String)obj); values.add(value); parameters.put(key, values); } } else parameters.put(key, value);
  6. 6. © Copyright Azul Systems 2016 My Stream Code (First Pass)  Two problems – Returns a null (ooh, bad!) – Does not URLDecoder.decode() like original code  decode() throws an exception private Map<String, List<String>> parseQuery(String query) { return (query == null) ? null : Arrays.stream(query.split("[&]")) .collect(groupingBy(s -> (s.split("[=]"))[0], mapping(s -> (s.split("[=]"))[1], toList()))); }
  7. 7. © Copyright Azul Systems 2016 My Stream Code (Updated) AbstractMap.SimpleImmutableEntry<String, String> splitKeyValue(String keyValue) { String[] parts = keyValue.split(“[=]”); try { return new AbstractMap.SimpleImmutableEntry<>( URLDecoder.decode(parts[0], ENC), URLDecoder.decode(parts[1], ENC)); } catch (UnsupportedEncodingException uee) { logger.warning(“Exception processing key-value pair”); return null; } }
  8. 8. © Copyright Azul Systems 2016 My Stream Code (Updated) private Map<String, List<String>> parseQuery(String query) { return (query == null) ? new HashMap<String, List<String>>() : Arrays.stream(query.split("[&]")) .map(this::splitKeyValue) .filter(e -> e != null) .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList()))); }
  9. 9. © Copyright Azul Systems 2016 Stream Reuse (Good or Bad?) IntStream stream = IntStream.of(1, 10); stream.forEach(System.out::println); /* Efficient use of resources? */ stream.forEach(System.out::println); IllegalStateException: stream has already been operated upon or closed Streams are single use
  10. 10. © Copyright Azul Systems 2016 Filter Confusion Files.walk(Paths.get(".")) .filter(p -> !p.toFile().getName().startsWith(".")) .forEach(System.out::println); Stream contains all files and directories Only checking the file name (not parent directory)foo bar .home/baz
  11. 11. © Copyright Azul Systems 2016 Don’t Forget The Terminator IntStream.range(1, 5) .peek(System.out::println) .peek(i -> { if (i == 5) throw new RuntimeException("bang!"); }); System.out.println(“Done!”); Done!
  12. 12. © Copyright Azul Systems 2016 Streams Can Be Infinite Even when you don’t want them to be IntStream.iterate(0, i -> (i + 1) % 2) .distinct() .limit(10) .forEach(System.out::println); Infinite stream of 0, 1, 0, 1... Only 0 and 1 10 elements will never be found in the stream Prints 0 1 (But code never proceeds)
  13. 13. © Copyright Azul Systems 2016 13 Concatenate the first word of each string in a List Simple Exercise StringBuilder sb = new StringBuilder(); input.forEach(s -> sb.append(s.charAt(0))); String result = sb.toString(); String result = input.stream() .map(s -> s.substring(0, 1)) .reduce("", (a, b) -> a + b);
  14. 14. © Copyright Azul Systems 2016 Simple Exercise 14 StringBuilder sb = new StringBuilder(); Optional<String> optResult = input.stream() .map(s -> s.substring(0, 1) .reduce((a, b) -> sb.append(b).toString()); String result = optResult.orElse(“”); FAIL!! result = “bcdef”
  15. 15. © Copyright Azul Systems 2016 15 Simple Exercise StringBuilder sb = new StringBuilder(); Optional<String> optResult = input.stream() .map(s -> s.substring(0, 1) .reduce((a, b) -> { if (a.length() == 1) sb.append(a); return sb.append(b).toString(); }); String result = optResult.orElse(“”);
  16. 16. © Copyright Azul Systems 2016 16 Simple Exercise String result = input.stream() .map(s -> s.substring(0, 1)) .collect(Collector.of( StringBuilder::new, // Supplier StringBuilder::append, // Accumulator StringBuilder::append, // Combiner StringBuilder::toString, // Finisher new Collector.Characteristics[0]));
  17. 17. © Copyright Azul Systems 2016 17 Simple Exercise String result = input.stream() .map(s -> s.substring(0, 1)) .collect(Collectors.joining());
  18. 18. © Copyright Azul Systems 2016 Which Is Better, And Why? 18 points.stream() .filter(p -> p.isInView()) .map(p -> p.invert()) .forEach(p -> System.out.println(p)); points.stream() .filter(GraphicPoint::isInView) .map(GraphicPoint::invert) .forEach(System.out::println);
  19. 19. © Copyright Azul Systems 2016 Parallel Streams Are Faster, Right?  Not guaranteed  A parallel stream will definitey do more work – It just might finish quicker  Parallel streams use the common fork-join pool – Defaults to number of threads = number of CPUs
  20. 20. © Copyright Azul Systems 2016 Nesting Parallel Streams List<Review> result = reviews.parallelStream() .filter(review -> review.getImages() .stream() .parallel() .anyMatch(image -> image.getIdImage() == 123)) .collect(Collectors.toList()); This will work, but will probably give worse performance than using a serial stream
  21. 21. © Copyright Azul Systems 2016 Multi-Sort (The Bad) patientRecords.stream() .filter(this::isValidRecord) .sorted(comparing(Patient::getName)) .sorted(comparing(Patient::getPhysician)) .sorted(comparing(Patient::getMedication)) .collect(toList());
  22. 22. © Copyright Azul Systems 2016 Multi-Sort (The Good) patientRecords.stream() .filter(this::isValidRecord) .sorted(comparing(Patient::getMedication) .thenComparing(comparing(Patient::getPhysician)) .thenComparing(comparing(Patient::getName))) .collect(toList());
  23. 23. © Copyright Azul Systems 2016 Imperative Streams LongAdder newMethodCount = new LongAdder(); map.get(class).stream() .forEach(method -> { output.println(val); if (isNewMethod(class, method)) newMethodCount.increment(); });
  24. 24. © Copyright Azul Systems 2016 A Bit More 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
  25. 25. © Copyright Azul Systems 2016 Almost Functional (But Not Quite) 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
  26. 26. © Copyright Azul Systems 2016 Conclusions  Streams are powerful – With great power comes great responsibility  Try not to think imperatively – forEach is good, but not for each (and every) situation  Part of streams power is clarity of code – That doesn’t mean as few lines as possible!  Parallel does not guarantee faster 26
  27. 27. © Copyright Azul Systems 2016 © Copyright Azul Systems 2015 @speakjava Streams in JDK 8: The Good, The Bad & The Ugly Simon Ritter Deputy CTO, Azul Systems 27

×