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.

Autumn collection JavaOne 2014

11,672 views

Published on

The slides of my talk at JavaOne 2014.

Published in: Education
  • Be the first to comment

Autumn collection JavaOne 2014

  1. 1. @JosePaumard #J1LBDS Autumn Collections From Iterable to Lambdas, Streams and Collectors
  2. 2. @JosePaumard #J1LBDS
  3. 3. @JosePaumard #J1LBDS Published November 10, 1994
  4. 4. @JosePaumard #J1LBDS Still available hardover, paperback, multimedia CD Published November 10, 1994
  5. 5. #J1LBDS @JosePaumard Why were they introduced ?
  6. 6. #J1LBDS @JosePaumard Why were they introduced ? How will they change the way we build applications ?
  7. 7. #J1LBDS @JosePaumard
  8. 8. #J1LBDS @JosePaumard
  9. 9. #J1LBDS @JosePaumard
  10. 10. #J1LBDS @JosePaumard Questions ? #J1LBDS
  11. 11. #J1LBDS @JosePaumard Let’s introduce the lambdas
  12. 12. #J1LBDS @JosePaumard Let’s introduce the lambdas on a simple example
  13. 13. #J1LBDS @JosePaumard A very simple example public class Person { private String name ; private int age ; // constructors // getters / setters } List<Person> list = new ArrayList<>() ; A plain old bean… … and a good old list
  14. 14. #J1LBDS @JosePaumard int sum = 0 ; // I need a default value in case // the list is empty int average = 0 ; for (Person person : list) { sum += person.getAge() ; } if (!list.isEmpty()) { average = sum / list.size() ; } Average of the ages
  15. 15. #J1LBDS @JosePaumard int sum = 0 ; int n = 0 ; int average = 0 ; for (Person person : list) { if (person.getAge() > 20) { n++ ; sum += person.getAge() ; } } if (n > 0) { average = sum / n ; } Trickier : average of the ages of people older than 20
  16. 16. #J1LBDS @JosePaumard Trickier : average of the ages of people older than 20 « imperative programming » int sum = 0 ; int n = 0 ; int average = 0 ; for (Person person : list) { if (person.getAge() > 20) { n++ ; sum += person.getAge() ; } } if (n > 0) { average = sum / n ; }
  17. 17. #J1LBDS @JosePaumard select avg(age) from Person where age > 20 … it does not have to be like that ! This is a description of the result
  18. 18. #J1LBDS @JosePaumard … it does not have to be like that ! This is a description of the result In that case, the DB server is free to compute the result the way it sees fit © SQL language, 1974 select avg(age) from Person where age > 20
  19. 19. #J1LBDS @JosePaumard Person age age > 20 sum 1st step : mapping map
  20. 20. #J1LBDS @JosePaumard Person age age > 20 sum 1st step : mapping Mapping : -takes a list of a given type -gives another list of a different type -same number of elements map
  21. 21. #J1LBDS @JosePaumard Person age age > 20 sum 2nd step : filtering filter map
  22. 22. #J1LBDS @JosePaumard Person age age > 20 sum 2nd step : filtering Filtering : -takes a list of a given type -gives another list of a the same type -less elements filter map
  23. 23. #J1LBDS @JosePaumard Person age age > 20 sum 3rd step : reduction reduce filter map
  24. 24. #J1LBDS @JosePaumard Person age age > 20 sum 3rd step : reduction Reduction : agregation of all the elements in a single one Ex : average, sum, min, max, etc… reduce filter map
  25. 25. #J1LBDS @JosePaumard How can I model that ?
  26. 26. #J1LBDS @JosePaumard The JDK 7 way Create an interface to model the mapper… public interface Mapper<T, V> { public V map(T t) ; }
  27. 27. #J1LBDS @JosePaumard Mapper<Person, Integer> mapper = new Mapper<Person, Integer>() { public Integer map(Person p) { return p.getAge() ; } } The JDK 7 way … and create an implementation public interface Mapper<T, V> { public V map(T t) ; }
  28. 28. #J1LBDS @JosePaumard Predicate<Integer> predicate = new Predicate<Integer>() { public boolean filter(Integer age) { return age > 20 ; } } The JDK 7 way We can do the same for filtering public interface Predicate<T> { public boolean filter(T t) ; }
  29. 29. #J1LBDS @JosePaumard Reducer<Integer> reduction = new Reducer<Integer>() { public Integer reduce(Integer i1, Integer i2) { return i1 + i2 ; } } The JDK 7 way And for the reduction public interface Reducer<T> { public T reduce(T t1, T t2) ; }
  30. 30. #J1LBDS @JosePaumard The JDK 7 way So the whole map / filter / reduce looks like this 1)Create 3 interfaces public interface Mapper<T, V> { public V map(T t) ; } public interface Predicate<T> { public boolean filter(T t) ; } public interface Reducer<T> { public T reduce(T t1, T t2) ; }
  31. 31. #J1LBDS @JosePaumard The JDK 7 way So the whole map / filter / reduce looks like this 1)Create 3 interfaces 2)Apply the pattern List<Person> persons = ... ; int sum = persons.map( new Mapper<Person, Integer>() { public Integer map(Person p) { return p.getAge() ; } }) .filter( new Filter<Integer>() { public boolean filter(Integer age) { return age > 20 ; } }) .reduce(0, new Reducer<Integer>() { public Integer reduce(Integer i1, Integer i2) { return i1 + i2 ; } } }) ;
  32. 32. #J1LBDS @JosePaumard The JDK 7 way So the whole map / filter / reduce looks like this 1)Create 3 interfaces 2)Apply the pattern List<Person> persons = ... ; int sum = persons.map( new Mapper<Person, Integer>() { public Integer map(Person p) { return p.getAge() ; } }) .filter( new Filter<Integer>() { public boolean filter(Integer age) { return age > 20 ; } }) .reduce(0, new Reducer<Integer>() { public Integer reduce(Integer i1, Integer i2) { return i1 + i2 ; } } }) ;
  33. 33. #J1LBDS @JosePaumard The JDK 8 way
  34. 34. #J1LBDS @JosePaumard The JDK 8 way Let’s rewrite our mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { return person.getAge() ; } }
  35. 35. #J1LBDS @JosePaumard The JDK 8 way Let’s rewrite our mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 method return person.getAge() ; } }
  36. 36. #J1LBDS @JosePaumard The JDK 8 way Let’s rewrite our mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 method return person.getAge() ; } } mapper = (Person person) we take a person p
  37. 37. #J1LBDS @JosePaumard The JDK 8 way Let’s rewrite our mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 method return person.getAge() ; } } mapper = (Person person) -> and then …
  38. 38. #J1LBDS @JosePaumard The JDK 8 way Let’s rewrite our mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 method return person.getAge() ; } } mapper = (Person person) -> person.getAge() ; … return the age of person
  39. 39. #J1LBDS @JosePaumard The JDK 8 way Let’s rewrite our mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 method return person.getAge() ; } } mapper = (Person person) -> person.getAge() ;
  40. 40. #J1LBDS @JosePaumard The JDK 8 way Let’s rewrite our mapper The compiler can recognize this as an implementation of Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 method return person.getAge() ; } } mapper = (Person person) -> person.getAge() ;
  41. 41. #J1LBDS @JosePaumard Some corner cases
  42. 42. #J1LBDS @JosePaumard What if … … there is more than one statement ? The return has to be explicit mapper = (Person person) -> { System.out.println("Mapping " + person) ; return person.getAge() ; }
  43. 43. #J1LBDS @JosePaumard What if … …I have no return value ? consumer = (Person person) -> p.setAge(p.getAge() + 1) ;
  44. 44. #J1LBDS @JosePaumard …I have more than one argument ? Or : reducer = (int i1, int i2) -> { return i1 + i2 ; } What if … reducer = (int i1, int i2) -> i1 + i2 ;
  45. 45. #J1LBDS @JosePaumard The JDK 8 way How can the compiler recognize the implementation of Mapper ? mapper = (Person person) -> person.getAge() ;
  46. 46. #J1LBDS @JosePaumard The JDK 8 way How can the compiler recognize the implementation of Mapper ? 1)mapper is of type Mapper mapper = (Person person) -> person.getAge() ;
  47. 47. #J1LBDS @JosePaumard The JDK 8 way How can the compiler recognize the implementation of Mapper ? 1)mapper is of type Mapper 2)There is only one method in Mapper mapper = (Person person) -> person.getAge() ;
  48. 48. #J1LBDS @JosePaumard The JDK 8 way How can the compiler recognize the implementation of Mapper ? 1)mapper is of type Mapper 2)There is only one method in Mapper 3)Both the parameters and the return types are compatible mapper = (Person person) -> person.getAge() ;
  49. 49. #J1LBDS @JosePaumard The JDK 8 way How can the compiler recognize the implementation of Mapper ? 1)mapper is of type Mapper 2)There is only one method in Mapper 3)Both the parameters and the return types are compatible 4)Thrown exceptions (if any are compatible) mapper = (Person person) -> person.getAge() ;
  50. 50. #J1LBDS @JosePaumard The JDK 8 way How can the compiler recognize the implementation of Mapper ? 1)mapper is of type Mapper 2)There is only one method in Mapper 3)Both the parameters and the return types are compatible 4)Thrown exceptions (if any are compatible) mapper = (Person person) -> person.getAge() ;
  51. 51. #J1LBDS @JosePaumard More lambdas Writing more lambdas becomes natural : mapper = (Person person) -> person.getAge() ; // mapper filter = (int age) -> age > 20 ; // filter reducer = (int i1, int i2) -> i1 + i2 ; // reducer
  52. 52. #J1LBDS @JosePaumard More lambdas And most of the time, the compiler understands this : The « parameter types » can be omitted mapper = person -> person.getAge() ; // mapper filter = age -> age > 20 ; // filter reducer = (i1, i2) -> i1 + i2 ; // reducer
  53. 53. #J1LBDS @JosePaumard Just a remark on the reduction How does it really work ?
  54. 54. #J1LBDS @JosePaumard
  55. 55. #J1LBDS @JosePaumard
  56. 56. #J1LBDS @JosePaumard
  57. 57. #J1LBDS @JosePaumard
  58. 58. #J1LBDS @JosePaumard
  59. 59. #J1LBDS @JosePaumard
  60. 60. #J1LBDS @JosePaumard
  61. 61. #J1LBDS @JosePaumard
  62. 62. #J1LBDS @JosePaumard
  63. 63. #J1LBDS @JosePaumard
  64. 64. #J1LBDS @JosePaumard Reduction Examples : Reducer r1 = (i1, i2) -> i1 + i2 ; // Ok
  65. 65. #J1LBDS @JosePaumard Reduction Examples : Reducer r1 = (i1, i2) -> i1 + i2 ; // Ok Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // Oooops Reducer r3 = (i1, i2) -> (i1 + i2)*(i1 + i2) ; // Aaaargh
  66. 66. #J1LBDS @JosePaumard Reduction Examples : Caveat : the result is always reproductible in serial it’s not in parallel Reducer r1 = (i1, i2) -> i1 + i2 ; // Ok Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // Oooops Reducer r3 = (i1, i2) -> (i1 + i2)*(i1 + i2) ; // Aaaargh Reducer r4 = (i1, i2) -> (i1 + i2) / 2 ; // Ouch !!
  67. 67. #J1LBDS @JosePaumard So far A lambda expression is an alternative to write instances of anonymous inner classes
  68. 68. #J1LBDS @JosePaumard Other syntaxes Very often one writes mapper = person -> person.getAge() ;
  69. 69. #J1LBDS @JosePaumard Other syntaxes Very often one writes But this syntax is also possible : « method reference » mapper = Person::getAge ; // non static method mapper = person -> person.getAge() ;
  70. 70. #J1LBDS @JosePaumard Other example : Or : sum = (i1, i2) -> i1 + i2 ; sum = Integer::sum ; // static method, new ! Other syntaxes max = (i1, i2) -> i1 > i2 ? i1 : i2 ; max = Integer::max ; // static method, new !
  71. 71. #J1LBDS @JosePaumard Other syntaxes Other example : toLower = String::toLowerCase ; // !!!! NO NO NO !!!! toLowerFR = String::toLowerCase(Locale.FRANCE) ;
  72. 72. #J1LBDS @JosePaumard More on the lambdas
  73. 73. #J1LBDS @JosePaumard 3 Questions about lambdas : What model for a lambda ?
  74. 74. #J1LBDS @JosePaumard 3 Questions about lambdas : What model for a lambda ? Can I put a lambda in a variable ?
  75. 75. #J1LBDS @JosePaumard 3 Questions about lambdas : What model for a lambda ? Can I put a lambda in a variable ? Is a lambda an object ?
  76. 76. #J1LBDS @JosePaumard Modelization A lambda is an instance of a « functional interface » @FunctionalInterface public interface Consumer<T> { public void accept(T t) ; }
  77. 77. #J1LBDS @JosePaumard Modelization A lambda is an instance of a « functional interface » -only one abstract method (methods from Object dont count) -can be annotated by @FunctionalInterface (optional) @FunctionalInterface public interface Consumer<T> { public void accept(T t) ; }
  78. 78. #J1LBDS @JosePaumard Putting a lambda in a variable Example of a consumer So : Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ; Consumer<String> c = s -> System.out.println(s) ;
  79. 79. #J1LBDS @JosePaumard Putting a lambda in a variable Example of a consumer So : Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ; Consumer<String> c = System.out::println ;
  80. 80. #J1LBDS @JosePaumard Questions : What model for a lambda ? answer : with a functional interface Can I put a lambda in a variable ? answer : yes Is a lambda an object ?
  81. 81. #J1LBDS @JosePaumard Is a lambda an objet ? Let’s play the game of 7 errors (with only 1 error) Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ; Consumer<String> c = s -> System.out.println(s) ;
  82. 82. #J1LBDS @JosePaumard Is a lambda an objet ? Let’s play the game of 7 errors (with only 1 error) Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ; Consumer<String> c = s -> System.out.println(s) ;
  83. 83. #J1LBDS @JosePaumard Questions : What model for a lambda ? answer : with a functionnal interface Can I put a lambda in a variable ? answer : yes Is a lambda an object ? answer : no
  84. 84. #J1LBDS @JosePaumard Plenty of lambdas : java.util.function
  85. 85. #J1LBDS @JosePaumard Java.util.functions This new package holds the functionnal interfaces There are 43 of them
  86. 86. #J1LBDS @JosePaumard Java.util.functions Supplier : alone in its kind Consumer / BiConsumer Function / BiFunction (UnaryOperator / BinaryOperator) Predicate / BiPredicate Plus the primitive types versions
  87. 87. #J1LBDS @JosePaumard Supplier A supplier supplies an object public interface Supplier<T> { T get() ; }
  88. 88. #J1LBDS @JosePaumard Consumer A consumer just … accepts an object public interface Consumer<T> { void accept(T t) ; }
  89. 89. #J1LBDS @JosePaumard Function A function takes an object and returns another one Can be chained and / or composed public interface Function<T, R> { R apply(T t) ; }
  90. 90. #J1LBDS @JosePaumard Predicate A Predicate takes an object and returns a boolean Can be negated, composed with and / or public interface Predicate<T> { boolean test(T t) ; }
  91. 91. #J1LBDS @JosePaumard Back to the map / filter / reduce pattern
  92. 92. #J1LBDS @JosePaumard The JDK 7 way How to apply map / filter / reduce to List<Person> ?
  93. 93. #J1LBDS @JosePaumard The JDK 7 way How to apply map / filter / reduce to List<Person> ? The legal way is to iterate over the elements and apply the pattern
  94. 94. #J1LBDS @JosePaumard The JDK 7 way How to apply map / filter / reduce to List<Person> ? The legal way is to iterate over the elements and apply the pattern One can create a helper method
  95. 95. #J1LBDS @JosePaumard Applying map to a List With a helper method, JDK 7 List<Person> persons = new ArrayList<>() ; List<Integer> ages = // ages is a new list Lists.map( persons, new Mapper<Person, Integer>() { public Integer map(Person p) { return p.getAge() ; } } ) ;
  96. 96. #J1LBDS @JosePaumard Applying map to a List With a helper method, JDK 8 & lambdas List<Person> persons = new ArrayList<>() ; List<Integer> ages = Lists.map( persons, person -> person.getAge() ) ;
  97. 97. #J1LBDS @JosePaumard Applying map to a List With a helper method, JDK 8 & lambdas List<Person> persons = new ArrayList<>() ; List<Integer> ages = Lists.map( persons, Person::getAge ) ;
  98. 98. #J1LBDS @JosePaumard Putting things together The map / filter / reduce pattern would look like this // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  99. 99. #J1LBDS @JosePaumard Putting things together The map / filter / reduce pattern would look like this The idea is to push lambdas to the API, and let it apply them on its content // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  100. 100. #J1LBDS @JosePaumard Putting things together The map / filter / reduce pattern would look like this Pro : the API can be optimized without touching the code : great ! // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  101. 101. #J1LBDS @JosePaumard Putting things together The map / filter / reduce pattern would look like this Pro : the API can be optimized without touching the code Cons … // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  102. 102. #J1LBDS @JosePaumard Putting things together 1) Suppose persons is a really BIG list // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  103. 103. #J1LBDS @JosePaumard Putting things together 1) Suppose persons is a really BIG list 2 duplications : ages & agesGT20 // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  104. 104. #J1LBDS @JosePaumard Putting things together 1) Suppose persons is a really BIG list 2 duplications : ages & agesGT20 What do we do with them ? Send them to the GC // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  105. 105. #J1LBDS @JosePaumard Putting things together 2) Suppose we have a allMatch() reducer allMatch is true if all the names are shorter than 20 chars let’s see a possible implementation of allMatch() // applying the map / filter / reduce pattern List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n -> n.length() < 20) ;
  106. 106. #J1LBDS @JosePaumard How could one write allMatch ? Here is a basic implementation of allMatch public static <T> boolean allMatch( List<? extends T> list, Filter<T> filter) { for (T t : list) { if (!filter.filter(t)) { return false ; } } return true ; }
  107. 107. #J1LBDS @JosePaumard How could one write allMatch ? Here is a basic implementation of allMatch public static <T> boolean allMatch( List<? extends T> list, Filter<T> filter) { for (T t : list) { if (!filter.filter(t)) { return false ; } } return true ; } No need to iterate over the whole list
  108. 108. #J1LBDS @JosePaumard Putting things together When we apply the allMatch() reducer… … the list names has already been evaluated ! // applying the map / filter / reduce pattern List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n -> n.length() < 20) ;
  109. 109. #J1LBDS @JosePaumard Putting things together When we apply the allMatch() reducer… … the list names has already been evaluated ! We lost a big opportunity for optimization ! // applying the map / filter / reduce pattern List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n -> n.length() < 20) ;
  110. 110. #J1LBDS @JosePaumard Putting things together When we apply the allMatch() reducer… … we should have wait to apply the filter A good way to do that : apply the filter step lazily ! // applying the map / filter / reduce pattern List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n -> n.length() < 20) ;
  111. 111. #J1LBDS @JosePaumard Conclusion Pro : 1 Cons : 3 (at least) // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  112. 112. #J1LBDS @JosePaumard Conclusion Pro : 1 Cons : 3 (at least) // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = Lists.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = Lists.filter(ages, a -> a > 20) ; int sum = Lists.reduce(agesGT20, Integer::sum) ;
  113. 113. #J1LBDS @JosePaumard Conclusion And the same goes for this one // applying the map / filter / reduce pattern List<Person> persons = ... ; List<Integer> ages = persons.map( persons, p -> p.getAge()) ; List<Integer> agesGT20 = ages.filter(ages, a -> a > 20) ; int sum = agesGT20.reduce(agesGT20, Integer::sum) ;
  114. 114. #J1LBDS @JosePaumard What about « in place » operations ? Not possible for the map step, because the type of the list changes Could be evaluated for the filter, with concurrency issues Doesnt make sense for the reduction
  115. 115. #J1LBDS @JosePaumard Conclusion (again) We need a new concept to handle BIG lists efficiently
  116. 116. #J1LBDS @JosePaumard Conclusion (again) We need a new concept to handle BIG lists efficiently The Collection framework is not the right one…
  117. 117. #J1LBDS @JosePaumard Conclusion (again) We need a new concept to handle BIG lists efficiently The Collection framework is not the right one We need something else !
  118. 118. #J1LBDS @JosePaumard So what pattern to choose ?
  119. 119. #J1LBDS @JosePaumard Introduction Putting map / filter / reduce methods on Collection would have led to : And even if it doesnt lead to viable patterns, it is still a pleasant way of writing things // map / filter / reduce pattern on collections int sum = persons .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  120. 120. #J1LBDS @JosePaumard Introduction Let’s keep the same kind of pattern and add a stream() // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  121. 121. #J1LBDS @JosePaumard Introduction Collection.stream() returns a Stream : new interface New interface = our hands are free ! // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  122. 122. #J1LBDS @JosePaumard New Collection interface So we need a new method on Collection public interface Collection<E> { // our good old methods Stream<E> stream() ; }
  123. 123. #J1LBDS @JosePaumard New Collection interface Problem : ArrayList doesnt compile anymore… Something has to be done ! public interface Collection<E> { // our good old methods Stream<E> stream() ; }
  124. 124. #J1LBDS @JosePaumard Interfaces We need to be able to add methods on interfaces… … without providing the implementation in the implementing classes!
  125. 125. #J1LBDS @JosePaumard Interfaces We need to be able to add methods on interfaces… … without providing the implementation in the implementing classes! Not possible in Java 7
  126. 126. #J1LBDS @JosePaumard Interfaces We need to be able to add methods on interfaces… … without providing the implementation in the implementing classes! Not possible in Java 7 Solution: change the way interfaces work in Java
  127. 127. #J1LBDS @JosePaumard New interfaces
  128. 128. #J1LBDS @JosePaumard Java 8 interfaces ArrayList needs to know the implementation of stream()… public interface Collection<E> { // our good old methods Stream<E> stream() ; }
  129. 129. #J1LBDS @JosePaumard Java 8 interfaces Let’s put it in the interface ! public interface Collection<E> { // our good old methods default Stream<E> stream() { return ... ; } }
  130. 130. #J1LBDS @JosePaumard Java 8 interfaces Let’s introduce default methods in Java Default methods are about interface evolution Allows the extension of old interfaces public interface Collection<E> { // our good old methods default Stream<E> stream() { return ... ; } }
  131. 131. #J1LBDS @JosePaumard Interfaces vs Abstract classes Does an interface become the same as an abstract class ?
  132. 132. #J1LBDS @JosePaumard Interfaces vs Abstract classes Does an interface become the same as an abstract class ? No! -an abstract class can have field, ie a state -an abstract class has a constructor
  133. 133. #J1LBDS @JosePaumard Default methods Does it bring multiple inheritance to Java ?
  134. 134. #J1LBDS @JosePaumard Default methods Does it bring multiple inheritance to Java ? Yes ! But we already have it
  135. 135. #J1LBDS @JosePaumard Default methods Does it bring multiple inheritance to Java ? Yes ! But we already have it public class String implements Serializable, Comparable<String>, CharSequence { // ... }
  136. 136. #J1LBDS @JosePaumard Default methods What we have so far is multiple inheritance of type
  137. 137. #J1LBDS @JosePaumard Default methods What we have so far is multiple inheritance of type What we get in Java 8 is multiple inheritance of behavior
  138. 138. #J1LBDS @JosePaumard Default methods What we have so far is multiple inheritance of type What we get in Java 8 is multiple inheritance of behavior What we dont have is multiple inheritance of state
  139. 139. #J1LBDS @JosePaumard Default methods What we have so far is multiple inheritance of type What we get in Java 8 is multiple inheritance of behavior What we dont have is multiple inheritance of state … and this is where the trouble is !
  140. 140. #J1LBDS @JosePaumard Default methods Will we have conflicts ?
  141. 141. #J1LBDS @JosePaumard Default methods Will we have conflicts ? Yes, so we need rules to handle them
  142. 142. #J1LBDS @JosePaumard Default methods public class C implements A, B { // ... }
  143. 143. #J1LBDS @JosePaumard Default methods Example #1 public interface A { default String a() { ... } } public interface B { default String a() { ... } } public class C implements A, B { // ... }
  144. 144. #J1LBDS @JosePaumard Default methods Example #1 Compile time error : class C inherits unrelated defaults for a() from types A and B public interface A { default String a() { ... } } public interface B { default String a() { ... } } public class C implements A, B { // ... }
  145. 145. #J1LBDS @JosePaumard Default methods Rule #1 Class wins ! No default if an implementation is present public interface A { default String a() { ... } } public interface B { default String a() { ... } } public class C implements A, B { public String a() { ... } }
  146. 146. #J1LBDS @JosePaumard Default methods Rule #2 A is more specific than B : A.a() is chosen public interface A extends B { default String a() { ... } } public interface B { default String a() { ... } } public class C implements A, B { // ... }
  147. 147. #J1LBDS @JosePaumard Default methods Back to rule #1 We can also make an explicit call public class C implements A, B { public String a() { return B.super(a) ; } } public interface A { default String a() { ... } } public interface B { default String a() { ... } }
  148. 148. #J1LBDS @JosePaumard 2 simple rules To handle the conflict of multiple inheritance of behavior
  149. 149. #J1LBDS @JosePaumard 2 simple rules To handle the conflict of multiple inheritance of behavior 1)Class wins !
  150. 150. #J1LBDS @JosePaumard 2 simple rules To handle the conflict of multiple inheritance of behavior 1)Class wins ! 2)More specific interfaces wins over less specific
  151. 151. #J1LBDS @JosePaumard 2 simple rules To handle the conflict of multiple inheritance of behavior 1)Class wins ! 2)More specific interfaces wins over less specific
  152. 152. #J1LBDS @JosePaumard An example Everybody who writes an implementation of Iterator writes this : public class MyIterator implements Iterator<E> { // some critical business code public void remove() { throw new UnsupportedOperationException("Naah !!!!!!") ; } ; }
  153. 153. #J1LBDS @JosePaumard An example Thanks to Java 8 : No silly implementation of remove() anymore ! public interface Iterator<E> { default void remove() { throw new UnsupportedOperationException("remove") ; } ; }
  154. 154. #J1LBDS @JosePaumard So the concept of « interface » changed in Java 8 public class HelloWorld { // souvenir souvenir public static void main(String[] args) { System.out.println("Hello world!") ; } ; } Java 8 interfaces
  155. 155. #J1LBDS @JosePaumard Java 8 interfaces So the concept of « interface » changed in Java 8 I mean, really public interface HelloWorld { // souvenir souvenir public static void main(String[] args) { System.out.println("Hello world!") ; } ; }
  156. 156. #J1LBDS @JosePaumard Java 8 interfaces 1)Default methods, multiple behavior inheritance Rules to handle conflicts, solved at compile time 2)Static methods in interfaces Will bring new patterns and new ways to write APIs
  157. 157. #J1LBDS @JosePaumard Where are we ? 1)We have a new syntax 2)We have a new pattern to implement 3)We have new interfaces to keep the backward compatibily
  158. 158. #J1LBDS @JosePaumard The Stream API
  159. 159. #J1LBDS @JosePaumard What is a Stream From a technical point of view : a typed interface « a stream of String »
  160. 160. #J1LBDS @JosePaumard What is a Stream From a technical point of view : a typed interface Other classes for primitive types : « a stream of String » IntStream, LongStream, DoubleStream
  161. 161. #J1LBDS @JosePaumard It’s a new notion It looks like a collection, but… -It is built on a « source », that can be a collection -No data needs to be provided at build time -No limit on what the source can produce
  162. 162. #J1LBDS @JosePaumard It’s a new notion A stream can be built on : -A collection or an array -An iterator -An I/O source Some of those can be « infinite »
  163. 163. #J1LBDS @JosePaumard It’s a new notion 1)A stream doesnt hold any data It’s just an object on which one can declare operations Streams are about « specifying computations »
  164. 164. #J1LBDS @JosePaumard It’s a new notion 1)A stream doesnt hold any data 2)A stream cant modify its source Consequence : it can use parallel processing
  165. 165. #J1LBDS @JosePaumard It’s a new notion 1)A stream doesnt hold any data 2)A stream cant modify its source 3)A source can be infinite So we need a way to guarantee that computations will be conducted in finite time
  166. 166. #J1LBDS @JosePaumard It’s a new notion 1)A stream doesnt hold any data 2)A stream cant modify its source 3)A source can be infinite 4)A stream works lazily Then optimizations can be made among the different operations We need a way / convention to trigger the computations
  167. 167. #J1LBDS @JosePaumard How can we build a stream ? Many ways… 1)From a collection : method Collection.stream Collection<String> collection = ... ; Stream<String> stream = collection.stream() ;
  168. 168. #J1LBDS @JosePaumard How can we build a stream ? Many ways… 1)From a collection : method Collection.stream 2)From an array : Arrays.stream(Object []) Stream<String> stream2 = Arrays.stream(new String [] {"one", "two", "three"}) ;
  169. 169. #J1LBDS @JosePaumard How can we build a stream ? Many ways… 1)From a collection : method Collection.stream 2)From an array : Arrays.stream(Object []) 3)From factory methods in Stream, IntStream, … Stream<String> stream1 = Stream.of("one", "two", "three") ;
  170. 170. #J1LBDS @JosePaumard How can we build a stream ? Some last patterns Stream.empty() ; // returns an empty Stream Stream.of(T t) ; // a stream with a single element Stream.generate(Supplier<T> s) ; Stream.iterate(T seed, UnaryOperator<T> f) ;
  171. 171. #J1LBDS @JosePaumard How can we build a stream ? Other ways scattered in the JDK string.chars() ; // returns a IntStream lineNumberReader.lines() ; // returns a Stream<String> random.ints() ; // return a IntStream
  172. 172. #J1LBDS @JosePaumard Back to our map / filter / reduce example // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ; A 1st example Builds a stream on a List
  173. 173. #J1LBDS @JosePaumard A 1st example Back to our map / filter / reduce example // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ; Declares operations
  174. 174. #J1LBDS @JosePaumard A 1st example Back to our map / filter / reduce example // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ; Triggers the computation
  175. 175. #J1LBDS @JosePaumard A 1st example Back to our map / filter / reduce example // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ; Default value, in case the stream is empty
  176. 176. #J1LBDS @JosePaumard 3 things on Streams
  177. 177. #J1LBDS @JosePaumard 1) Two types of operations One can declare operations on a Stream Two types of operations : 1)Intermediate operations : declarations, processed lazily ex : map, filter
  178. 178. #J1LBDS @JosePaumard 1) Two types of operations One can declare operations on a Stream Two types of operations : 1)Intermediate operations : declarations, processed lazily ex : map, filter 2)Terminal operations : trigger the computations ex : reduce
  179. 179. #J1LBDS @JosePaumard 2) State of a stream The implementation of Stream has a state : -SIZED : the number of elements is known -ORDERED : the order matters (List) -DISTINCT : all elements are unique (Set) -SORTED : all elements have been sorted (SortedSet)
  180. 180. #J1LBDS @JosePaumard 2) State of a stream Some operations change that : -filtering removes the SIZED -mapping removes DISTINCT and SORTED
  181. 181. #J1LBDS @JosePaumard 2) State of a stream It’s all about optimization! Collection<Person> collection = ... ; Stream<Person> stream = collection.stream() ; stream.distinct() .map(Person::getAge) .filter(a -> a > 20) .reduce(0, Integer::sum) ;
  182. 182. #J1LBDS @JosePaumard 2) State of a stream It’s all about optimization! Collection<Person> collection = new HashSet<>() ; Stream<Person> stream = collection.stream() ; // DISTINCT stream.distinct() // DISTINCT = will not do anything .map(Person::getAge) .filter(a -> a > 20) .reduce(0, Integer::sum) ;
  183. 183. #J1LBDS @JosePaumard 2) State of a stream It’s all about optimization! Collection<Person> collection = ... ; Stream<Person> stream = collection.stream() ; stream.sorted() .forEach(System.out::println) ;
  184. 184. #J1LBDS @JosePaumard 2) State of a stream It’s all about optimization! Collection<Person> collection = new SortedSet<>() ; Stream<Person> stream = collection.stream() ; // SORTED stream.sorted() // SORTED = doesnt do anything .forEach(System.out::println) ;
  185. 185. #J1LBDS @JosePaumard 3) Stateless / stateful operations Stateless / stateful operations Some operations are stateless : It means that they dont need more information than the one held by their parameters persons.stream().map(p -> p.getAge()) ;
  186. 186. #J1LBDS @JosePaumard 3) Stateless / stateful operations Stateless / stateful operations Some operations are stateless : Some needs to retain information : persons.stream().map(p -> p.getAge()) ; stream.limit(10_000_000) ; // select the 10M first elements
  187. 187. #J1LBDS @JosePaumard Example Let’s sort an array of String Random rand = new Random() ; String [] strings = new String[10_000_000] ; for (int i = 0 ; i < strings.length ; i++) { strings[i] = Long.toHexString(rand.nextLong()) ; }
  188. 188. #J1LBDS @JosePaumard Example Let’s sort an array of String Soooo Java 7… Random rand = new Random() ; String [] strings = new String[10_000_000] ; for (int i = 0 ; i < strings.length ; i++) { strings[i] = Long.toHexString(rand.nextLong()) ; }
  189. 189. #J1LBDS @JosePaumard Example Let’s sort an array of String Better ? Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(rand.nextLong()) ) ;
  190. 190. #J1LBDS @JosePaumard Let’s sort an array of String Better ! // Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(ThreadLocalRandom.current().nextLong()) ) ; Example
  191. 191. #J1LBDS @JosePaumard Let’s sort an array of String Example // other way Stream<String> stream = ThreadLocalRandom .current() .longs() // returns a LongStream .mapToObj(l -> Long.toHexString(l)) ;
  192. 192. #J1LBDS @JosePaumard Let’s sort an array of String Example // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) ;
  193. 193. #J1LBDS @JosePaumard Let’s sort an array of String Example // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; T = 4 ms
  194. 194. #J1LBDS @JosePaumard Let’s sort an array of String Example // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ;
  195. 195. #J1LBDS @JosePaumard Let’s sort an array of String Example // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ; T = 14 s
  196. 196. #J1LBDS @JosePaumard Let’s sort an array of String Example // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ; T = 14 s Intermediate calls !
  197. 197. #J1LBDS @JosePaumard More on stream operations 1)There are intermediate and terminal operations 2)A stream is processed on a terminal operation call
  198. 198. #J1LBDS @JosePaumard More on stream operations 1)There are intermediate and terminal operations 2)A stream is processed on a terminal operation call 3)Only one terminal operation is allowed 4)It cannot be processed again If needed another stream should be built on the source
  199. 199. #J1LBDS @JosePaumard Parallel Streams
  200. 200. #J1LBDS @JosePaumard Optimization The first optimization (well, laziness was first !) we need is parallelism The fork / join enable parallel programming since JDK 7 But writing tasks is hard and does not always lead to better performances
  201. 201. #J1LBDS @JosePaumard Optimization The first optimization (well, laziness was first !) we need is parallelism The fork / join enable parallel programming since JDK 7 But writing tasks is hard and does not always lead to better performances Using the parallel stream API is much more secure
  202. 202. #J1LBDS @JosePaumard How to build a parallel stream ? Two patterns 1)Call parallelStream() instead of stream() 2)Call parallel() on an existing stream Stream<String> s = strings.parallelStream() ; Stream<String> s = strings.stream().parallel() ;
  203. 203. #J1LBDS @JosePaumard Can we decide the # of cores ? Answer is yes, but it’s tricky System.setProperty( "java.util.concurrent.ForkJoinPool.common.parallelism", 2) ;
  204. 204. #J1LBDS @JosePaumard Can we decide the # of cores ? Answer is yes, but it’s tricky List<Person> persons = ... ; ForkJoinPool fjp = new ForkJoinPool(2) ; fjp.submit( () -> // persons.stream().parallel() // .mapToInt(p -> p.getAge()) // Callable<Integer> .filter(age -> age > 20) // .average() // ).get() ;
  205. 205. #J1LBDS @JosePaumard Is parallelism really that simple ? Example : performances Code 1 List<Long> list = new ArrayList<>(10_000_100) ; for (int i = 0 ; i < 10_000_000 ; i++) { list.add(ThreadLocalRandom.current().nextLong()) ; }
  206. 206. #J1LBDS @JosePaumard Is parallelism really that simple ? Example : performances Code 2 Stream<Long> stream = Stream.generate(() -> ThreadLocalRandom.current().nextLong()) ; List<Long> list = stream.limit(10_000_000).collect(Collectors.toList()) ;
  207. 207. #J1LBDS @JosePaumard Is parallelism really that simple ? Example : performances Code 3 Stream<Long> stream = ThreadLocalRandom.current().longs(10_000_000).mapToObj(Long::new) ; List<Long> list = stream.collect(Collectors.toList()) ;
  208. 208. #J1LBDS @JosePaumard Is parallelism really that simple ? Example : performances Serial Parallel Code 1 (for) 270 ms Code 2 (limit) 310 ms Code 3 (longs) 250 ms
  209. 209. #J1LBDS @JosePaumard Is parallelism really that simple ? Example : performances Serial Parallel Code 1 (for) 270 ms Code 2 (limit) 310 ms 500 ms Code 3 (longs) 250 ms 320 ms
  210. 210. #J1LBDS @JosePaumard Is parallelism really that simple ? A sneaky problem List<Person> persons = new ArrayList<>() ; // some code to add people persons.stream() .map(Person::getAge) .collect(Collectors.toList()) ;
  211. 211. #J1LBDS @JosePaumard Is parallelism really that simple ? A sneaky problem List<Person> persons = new ArrayList<>() ; // some code to add people persons.stream() // ORDERED ! .map(Person::getAge) .collect(Collectors.toList()) ;
  212. 212. #J1LBDS @JosePaumard Is parallelism really that simple ? A sneaky problem List<Person> persons = new ArrayList<>() ; // some code to add people persons.stream() .parallel() .map(Person::getAge) .collect(Collectors.toList()) ;
  213. 213. #J1LBDS @JosePaumard Is parallelism really that simple ? A sneaky problem List<Person> persons = new ArrayList<>() ; // some code to add people persons.stream() .unordered() .parallel() .map(Person::getAge) .collect(Collectors.toList()) ;
  214. 214. #J1LBDS @JosePaumard Parallelism Parallelism implies overhead most of the time Badly configured parallel operations can lead to unneeded computations that will slow down the overall process Unnecessary ordering of a stream will lead to performance hits
  215. 215. #J1LBDS @JosePaumard Reductions
  216. 216. #J1LBDS @JosePaumard A simple reduction Sum of ages // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  217. 217. #J1LBDS @JosePaumard It would be nice to be able to write : But there’s no sum() on Stream<T> What would be the sense of « adding persons » ? // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ; A simple reduction
  218. 218. #J1LBDS @JosePaumard A simple reduction It would be nice to be able to write : But there’s no sum() on Stream<T> There’s one on IntStream ! // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ;
  219. 219. #J1LBDS @JosePaumard A simple reduction 2nd version : Value if persons is an empty list : 0 // map / filter / reduce pattern on collections int sum = persons.stream() .map(Person::getAge) .filter(a -> a > 20) .mapToInt(Integer::intValue) .sum() ;
  220. 220. #J1LBDS @JosePaumard A simple reduction 2nd version : Value if persons is an empty list : 0 // map / filter / reduce pattern on collections int sum = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) // .mapToInt(Integer::intValue) .sum() ;
  221. 221. #J1LBDS @JosePaumard Default values
  222. 222. #J1LBDS @JosePaumard A simple reduction What about max() and min() ? How can one chose a default value ? // map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;
  223. 223. #J1LBDS @JosePaumard Problem with the default value The « default value » is a tricky notion
  224. 224. #J1LBDS @JosePaumard Problem with the default value The « default value » is a tricky notion 1)The « default value » is the reduction of the empty set
  225. 225. #J1LBDS @JosePaumard Problem with the default value The « default value » is a tricky notion 1)The « default value » is the reduction of the empty set 2)But it’s also the identity element for the reduction
  226. 226. #J1LBDS @JosePaumard
  227. 227. #J1LBDS @JosePaumard
  228. 228. #J1LBDS @JosePaumard
  229. 229. #J1LBDS @JosePaumard
  230. 230. #J1LBDS @JosePaumard
  231. 231. #J1LBDS @JosePaumard
  232. 232. #J1LBDS @JosePaumard
  233. 233. #J1LBDS @JosePaumard
  234. 234. #J1LBDS @JosePaumard Problem with the default value The « default value » is a tricky notion 1)The « default value » is the reduction of the empty set 2)But it’s also the identity element for the reduction
  235. 235. #J1LBDS @JosePaumard Problem with the default value Problem : max() and min() have no identity element eg : an element e for which max(e, a) = a
  236. 236. #J1LBDS @JosePaumard Problem with the default value Problem : max() and min() have no identity element eg : an element e for which max(e, a) = a 0 cant be, max(0, -1) is not -1…
  237. 237. #J1LBDS @JosePaumard Problem with the default value Problem : max() and min() have no identity element eg : an element e for which max(e, a) = a 0 cant be, max(0, -1) is not -1… −∞ is not an integer
  238. 238. #J1LBDS @JosePaumard Problem with the default value Problem : max() and min() have no identity element eg : an element e for which max(e, a) = a 0 cant be, max(0, -1) is not -1… −∞ is not an integer Integer.MIN_VALUE
  239. 239. #J1LBDS @JosePaumard Problem with the default value So what is the default value of max() and min() ?
  240. 240. #J1LBDS @JosePaumard Problem with the default value So what is the default value of max() and min() ? Answer : there is no default value for max() and min()
  241. 241. #J1LBDS @JosePaumard Problem with the default value So what is the returned type of the max() method ? // map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;
  242. 242. #J1LBDS @JosePaumard Problem with the default value So what is the returned type of the max() method ? If it’s int, then the default value will be 0… // map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;
  243. 243. #J1LBDS @JosePaumard Problem with the default value So what is the returned type of the max() method ? If it’s Integer, then the default value will be null… // map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;
  244. 244. #J1LBDS @JosePaumard Optionals Since there’s none, we need another notion « there might be no result » // map / filter / reduce pattern on collections OptionalInt optionalMax = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;
  245. 245. #J1LBDS @JosePaumard Optionals What can I do with OptionalInt ? 1st pattern : test if it holds a value OptionalInt optionalMax = ... ; int max ; if (optionalMax.isPresent()) { max = optionalMax.get() ; } else { max = ... ; // decide a « default value » }
  246. 246. #J1LBDS @JosePaumard Optionals What can I do with OptionalInt ? 2nd pattern : read the held value, can get an exception OptionalInt optionalMax = ... ; // throws NoSuchElementException if no held value int max = optionalMax.getAsInt() ;
  247. 247. #J1LBDS @JosePaumard Optionals What can I do with OptionalInt ? 3rd pattern : read the held value, giving a default value OptionalInt optionalMax = ... ; // get 0 if no held value int max = optionalMax.orElse(0) ;
  248. 248. #J1LBDS @JosePaumard Optionals What can I do with OptionalInt ? 4th pattern : read the held value, or throw an exception OptionalInt optionalMax = ... ; // exceptionSupplier will supply an exception, if no held value int max = optionalMax.orElseThrow(exceptionSupplier) ;
  249. 249. #J1LBDS @JosePaumard Available optionals Optional<T> OptionalInt, OptionalLong, OptionalDouble
  250. 250. #J1LBDS @JosePaumard Available reductions On Stream<T> : -reduce(), with different parameters -count(), min(), max() -anyMatch(), allMatch(), noneMatch() -findFirst(), findAny() -toArray() -forEach(), forEachOrdered()
  251. 251. #J1LBDS @JosePaumard Available reductions On IntStream, LongStream, DoubleStream : -average() -summaryStatistics()
  252. 252. #J1LBDS @JosePaumard Available reductions SummaryStatistics is an object that holds : -Count, Min, Max, Sum, Average It’s a consumer, so it can be easily extended to compute other statistics
  253. 253. #J1LBDS @JosePaumard Mutable reductions
  254. 254. #J1LBDS @JosePaumard Mutable reductions : example 1 Use of the helper class : Collectors List<String> strings = stream .map(Object::toString) .collect(Collectors.toList()) ;
  255. 255. #J1LBDS @JosePaumard Mutable reductions : example 2 Concatenating strings with a helper String names = persons .stream() .map(Person::getName) .collect(Collectors.joining()) ;
  256. 256. #J1LBDS @JosePaumard Mutable reductions Common things : -have a container : Collection or StringBuilder -have a way to add an element to the container -have a way to merge two containers (for parallelization)
  257. 257. #J1LBDS @JosePaumard Mutable reductions : example 1 Putting things together : ArrayList<String> strings = stream .map(Object::toString) .collect( () -> new ArrayList<String>(), // the supplier (suppList, s) -> suppList.add(s), // the accumulator (suppL1, suppL2) -> suppL1.addAll(suppL2) // the combiner ) ;
  258. 258. #J1LBDS @JosePaumard Mutable reductions : example 1 Putting things together : ArrayList<String> strings = stream .map(Object::toString) .collect( ArrayList::new, // the supplier ArrayList::add, // the accumulator ArrayList::addAll // the combiner ) ;
  259. 259. #J1LBDS @JosePaumard Mutable reductions : example 1 Putting things together : ArrayList<String> strings = stream .map(Object::toString) .collect( Collectors.toList() ) ;
  260. 260. #J1LBDS @JosePaumard Collectors
  261. 261. #J1LBDS @JosePaumard The Collectors class A rich toolbox (37 methods) for various types of reductions -counting, minBy, maxBy -summing, averaging, summarizing -joining -toList, toSet And - mapping, groupingBy, partionningBy
  262. 262. #J1LBDS @JosePaumard The Collectors class Average, Sum, Count persons .stream() .collect(Collectors.averagingDouble(Person::getAge)) ; persons .stream() .collect(Collectors.counting()) ;
  263. 263. #J1LBDS @JosePaumard The Collectors class Concatenating the names in a String String names = persons .stream() .map(Person::getName) .collect(Collectors.joining(", ")) ;
  264. 264. #J1LBDS @JosePaumard The Collectors class Accumulating in a List, Set Set<Person> setOfPersons = persons .stream() .collect( Collectors.toSet()) ;
  265. 265. #J1LBDS @JosePaumard The Collectors class Accumulating in a custom collection TreeSet<Person> treeSetOfPersons = persons .stream() .collect( Collectors.toCollection(TreeSet::new)) ;
  266. 266. #J1LBDS @JosePaumard The Collectors class Getting the max according to a comparator Bonus : new API to build comparators Optional<Person> optionalPerson = persons .stream() .collect( Collectors.maxBy( Comparator.comparing(Person::getAge)) ;
  267. 267. #J1LBDS @JosePaumard Building comparators New API to build comparators in a declarative way Comparator<Person> comp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person::getAge) ;
  268. 268. #J1LBDS @JosePaumard Building comparators New API to build comparators in a declarative way Comparator<Person> comp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person::getAge) ; Comparator<Person> comp = Comparator.comparing(Person::getLastName) .reversed() ;
  269. 269. #J1LBDS @JosePaumard Building comparators New API to build comparators in a declarative way Comparator<Person> comp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person::getAge) ; Comparator<Person> comp = Comparator.nullsFirst( Comparator.comparing(Person::getLastName) ) ;
  270. 270. #J1LBDS @JosePaumard The Collector API : groupingBy « Grouping by » builds hash maps -must explain how the keys are built -by default the values are put in a list -may specify a downstream (ie a collector)
  271. 271. #J1LBDS @JosePaumard The Collector API : groupingBy Grouping a list of persons by their age Map<Integer, List<Person>> map = persons .stream() .collect( Collectors.groupingBy(Person::getAge)) ;
  272. 272. #J1LBDS @JosePaumard The Collector API : groupingBy Grouping a list of persons by their age Map<Integer, Set<Person>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.toSet() // the downstream ) ;
  273. 273. #J1LBDS @JosePaumard The Collector API : groupingBy Grouping a list of persons names by their age Map<Integer, Set<String>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.mapping( // Person::getLastName, // the downstream Collectors.toSet() // ) ) ;
  274. 274. #J1LBDS @JosePaumard The Collector API : groupingBy Grouping a list of persons names by their age Map<Integer, TreeSet<String>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new) ) ) ;
  275. 275. #J1LBDS @JosePaumard The Collector API : groupingBy Grouping a list of blah blah blah TreeMap<Integer, TreeSet<String>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, TreeMap::new, Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new) ) ) ;
  276. 276. #J1LBDS @JosePaumard The Collector API : groupingBy Example : creating an age histogram Gives the # of persons by age Map<Integer, Long> map = persons .stream() .collect( Collectors.groupingBy(Person::getAge, Collectors.counting()) ) ;
  277. 277. #J1LBDS @JosePaumard The Collector API : partioningBy Creates a Map<Boolean, …> on a predicate -the map has 2 keys : TRUE and FALSE -may specify a downstream
  278. 278. #J1LBDS @JosePaumard The Collector API : partioningBy Creates a Map<Boolean, …> on a predicate map.get(TRUE) returns the list people older than 20 Map<Boolean, List<Person>> map = persons .stream() .collect( Collectors.partitioningBy(p -> p.getAge() > 20) ) ;
  279. 279. #J1LBDS @JosePaumard The Collector API : partioningBy Can further process the list of persons Map<Boolean, TreeSet<String>> map = persons .stream() .collect( Collectors.partitioningBy( p -> p.getAge() > 20, Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new)) ) ) ) ;
  280. 280. #J1LBDS @JosePaumard The Collector API : collectingAndThen Collect data with a downstream Then apply a function called a « finisher » Useful for putting the result in a immutable collection
  281. 281. #J1LBDS @JosePaumard The Collector API : collectingAndThen In this case « Map::entrySet » is a finisher Set<Map.Entry<Integer, List<Person>>> set = persons .stream() .collect( Collectors.collectingAndThen( Collectors.groupingBy( Person::getAge), // downstream, builds a map Map::entrySet // finisher, applied on the map ) ;
  282. 282. #J1LBDS @JosePaumard The Collector API : collectingAndThen Map<Integer, List<Person>> map = // immutable persons .stream() .collect( Collectors.collectingAndThen( Collectors.groupingBy( Person::getAge), // the downstream builds a map Collections::unmodifiableMap // finisher is applied to the // map ) ;
  283. 283. #J1LBDS @JosePaumard Some real examples https://github.com/JosePaumard/jdk8-lambda-tour
  284. 284. #J1LBDS @JosePaumard Conclusion
  285. 285. #J1LBDS @JosePaumard Conclusion Why are lambdas introduced in Java 8 ?
  286. 286. #J1LBDS @JosePaumard Conclusion Why are lambdas introduced in Java 8 ? Because it’s in the mood !
  287. 287. #J1LBDS @JosePaumard
  288. 288. #J1LBDS @JosePaumard Conclusion Why are lambdas introduced in Java 8 ? Because it’s in the mood !
  289. 289. #J1LBDS @JosePaumard Conclusion Why are lambdas introduced in Java 8 ? Because it’s in the mood ! Because it allows to write more compact code !
  290. 290. #J1LBDS @JosePaumard Compact code is better ! An example of compact code (in C) http://www.codeproject.com/Articles/2228/Obfuscating-your-Mandelbrot-code #include "stdio.h" main() { int b=0,c=0,q=60,_=q;for(float i=-20,o,O=0,l=0,j,p;j=O*O,p=l*l, (!_--|(j+p>4)?fputc(b?q+(_/3):10,(i+=!b,p=j=O=l=0,c++,stdout)), _=q:l=2*O*l+i/20,O=j-p+o),b=c%q,c<2400;o=-2+b*.05) ; }
  291. 291. #J1LBDS @JosePaumard Compact code is better ! An example of compact code (in C) http://www.codeproject.com/Articles/2228/Obfuscating-your-Mandelbrot-code #include "stdio.h" main() { int b=0,c=0,q=60,_=q;for(float i=-20,o,O=0,l=0,j,p;j=O*O,p=l*l, (!_--|(j+p>4)?fputc(b?q+(_/3):10,(i+=!b,p=j=O=l=0,c++,stdout)), _=q:l=2*O*l+i/20,O=j-p+o),b=c%q,c<2400;o=-2+b*.05) ; } ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++ ++ +++++++++++ +++++++++++++++++++++++++++++++++++ +++++++ ++++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++++ ++++++++ ++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++ +++++ +++++++++++++++++++++++++++++++++ ++++++ +++++++++++++++++++++++ + +++++ ++++++ +++++++++++++++++++++++ ++ ++++++ ++++++++++++++++++++++ + ++++++ ++++++++++++++++++++++ + ++++++ ++++++++++++++++++++ + + +++++++ ++++++ ++++++++ ++++++++++++++++++++ + + +++++++ ++++++++++++++++++++++ + ++++++ ++++++++++++++++++++++ + ++++++ +++++++++++++++++++++++ ++ ++++++ +++++++++++++++++++++++ + +++++ ++++++ +++++++++++++++++++++++++++++++++ ++++++ +++++++++++++++++++++++++++++++++ +++++ ++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++++ ++++++++ ++++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++++ +++++++ ++++++++++++++++++++++++++++++++++++ ++ +++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  292. 292. #J1LBDS @JosePaumard Conclusion Why are lambdas introduced in Java 8 ? Because it’s in the mood ! Because it allows to write more compact code !
  293. 293. #J1LBDS @JosePaumard Conclusion Why are lambdas introduced in Java 8 ? Because it brings new and efficient patterns, that enable easy and safe parallelization, -needed by modern applications -needed by API writers
  294. 294. #J1LBDS @JosePaumard Conclusion Java 8 is here, it’s the biggest update in 15 years
  295. 295. #J1LBDS @JosePaumard Conclusion Java 8 is here, it’s the biggest update in 15 years Moving to Java 8 means a lot of work for us developers ! -Self training -Changing our habits
  296. 296. #J1LBDS @JosePaumard Conclusion Java 8 is here, it’s the biggest update in 15 years Moving to Java 8 means a lot of work for us developers ! -Self training -Changing our habits -Convincing our bosses (sigh…)
  297. 297. #J1LBDS @JosePaumard Conclusion Books are there ! available available available available available
  298. 298. #J1LBDS @JosePaumard Conclusion Java 8 is the biggest update in 15 years « Java is a blue collar language. It’s not PhD thesis material but a language for a job » – James Gosling, 1997
  299. 299. #J1LBDS @JosePaumard Conclusion Java 8 is the biggest update in 15 years « Language features are not a goal unto themselves; language features are enablers, encouraging or discouraging certain styles and idioms » – Brian Goetz, 2013
  300. 300. #J1LBDS @JosePaumard Conclusion Java 8 is the biggest update in 15 years The good news is : Java is still Java !
  301. 301. @JosePaumard #J1LBDS
  302. 302. @JosePaumard #J1LBDS

×