330
JDK 8 & Lambdas Lambdas, Streams, Collectors

JDK 8, lambdas, streams, collectors - Bretagne Tour

Embed Size (px)

DESCRIPTION

L'API la plus utilisée du JDK est sans aucun doute l'API Collection. Brillamment conçue il y a un peu plus de 15 ans, elle est encore aujourd'hui au coeur de toutes les applications Java. En 2004, elle a subi son premier lifting, avec l'introduction des génériques. Cette mise à jour, bien qu'importante, n'a cependant pas modifié ses patterns d'utilisation. Avec l'introduction des lambdas en Java 8, l'API Collection est à nouveau réécrite, mais cette fois la situation est différente : ses patterns d'utilisation sont complètement changés. La première partie de cette conférence introduit les lambda expressions, comment les écrire, et ce qu'elle nous apportent en tant que développeurs. La deuxième partir présente en détail les nouveaux patterns introduits par les API Stream et Collector. Ces nouvelles API vont changer la façon dont nous allons pouvoir traiter les collections de grande taille, y compris en parallèle, avec un modèle de programmation très simple, et des patterns très puissants. Cette puissance sera montrée dans des exemples réels, qui monteront comment Java 8 va pouvoir nous aider à écrire simplement du code efficace et performant.

Citation preview

Page 1: JDK 8, lambdas, streams, collectors - Bretagne Tour

JDK 8 & Lambdas

Lambdas, Streams, Collectors

Page 2: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 3: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 4: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pourquoi ont-ils été introduits ?

Page 5: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pourquoi ont-ils été introduits ?

Pourquoi sont-ils si importants ?

Page 6: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pourquoi ont-ils été introduits ?

Pourquoi sont-ils si importants ?

Comment vont-ils changer nos habitudes de

programmation ?

Page 7: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 8: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 9: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 10: JDK 8, lambdas, streams, collectors - Bretagne Tour

Introduisons les lambdas

Page 11: JDK 8, lambdas, streams, collectors - Bretagne Tour

Introduisons les lambdas sur un exemple simple

Page 12: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un exemple très simple

public class Person { private String name ; private int age ; // constructors // getters / setters }

List<Person> list = new ArrayList<>() ;

Un bon vieux bean

… et une bonne vieille liste

Page 13: JDK 8, lambdas, streams, collectors - Bretagne Tour

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() ; }

Calculons la moyenne des âges des personnes

Page 14: JDK 8, lambdas, streams, collectors - Bretagne Tour

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 ; }

Plus dur : pour les personnes de plus de 20 ans

Page 15: JDK 8, lambdas, streams, collectors - Bretagne Tour

« programmation impérative »

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 ; }

Plus dur : pour les personnes de plus de 20 ans

Page 16: JDK 8, lambdas, streams, collectors - Bretagne Tour

select avg(age) from Person where age > 20

… pas une obligation !

Ici on décrit le résultat

Page 17: JDK 8, lambdas, streams, collectors - Bretagne Tour

select avg(age) from Person where age > 20

Dans ce cas la base de données mène le calcul

comme elle l’entend

© SQL 1974

… pas une obligation !

Ici on décrit le résultat

Page 18: JDK 8, lambdas, streams, collectors - Bretagne Tour

Person age age > 20 sum

1ère étape : mapping

map

Page 19: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mapping :

- prend une liste d’un type donné

- retourne une liste d’un autre type

- possède le même nombre d’éléments

1ère étape : mapping

map

Person age age > 20 sum

Page 20: JDK 8, lambdas, streams, collectors - Bretagne Tour

2ème étape : filtrage

filter map

Person age age > 20 sum

Page 21: JDK 8, lambdas, streams, collectors - Bretagne Tour

Filtrage :

- prend une liste d’un type donné

- retourne une liste du même type

- mais avec moins d’éléments

Person age age > 20 sum

2ème étape : filtrage

filter map

Page 22: JDK 8, lambdas, streams, collectors - Bretagne Tour

3ème étape : réduction

reduce filter map

Person age age > 20 sum

Page 23: JDK 8, lambdas, streams, collectors - Bretagne Tour

Reduction : agrégation des éléments d’une

liste dans un seul élément

Ex : moyenne, somme, min, max, etc…

Person age age > 20 sum

3ème étape : réduction

reduce filter map

Page 24: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment peut-on

modéliser ce traitement ?

Page 25: JDK 8, lambdas, streams, collectors - Bretagne Tour

La façon JDK 7

On crée une interface pour modéliser le mapper…

public interface Mapper<T, V> { public V map(T t) ; }

Page 26: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mapper<Person, Integer> mapper = new Mapper<Person, Integer>() { public Integer map(Person p) { return p.getAge() ; } }

La façon JDK 7

… et on crée une classe anonyme

public interface Mapper<T, V> { public V map(T t) ; }

Page 27: JDK 8, lambdas, streams, collectors - Bretagne Tour

La façon JDK 7

On peut faire la même chose pour le filtrage

public interface Predicate<T> { public boolean filter(T t) ; }

Page 28: JDK 8, lambdas, streams, collectors - Bretagne Tour

AgePredicate predicate = new Predicate<Integer>() { public boolean filter(Integer i) { return i > 20 ; } }

public interface Predicate<T> { public boolean filter(T t) ; }

La façon JDK 7

On peut faire la même chose pour le filtrage

Page 29: JDK 8, lambdas, streams, collectors - Bretagne Tour

Et enfin pour la réduction

public interface Reducer<T> { public T reduce(T t1, T t2) ; }

La façon JDK 7

Page 30: JDK 8, lambdas, streams, collectors - Bretagne Tour

Reducer<Integer> reduction = new Reducer<Integer>() { public Integer reduce(Integer i1, Integer i2) { return i1 + i2 ; } }

public interface Reducer<T> { public T reduce(T t1, T t2) ; }

La façon JDK 7

Et enfin pour la réduction

Page 31: JDK 8, lambdas, streams, collectors - Bretagne Tour

Au final, le pattern map / filter / reduce en JDK 7 :

1) Créer 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) ; }

La façon JDK 7

Page 32: JDK 8, lambdas, streams, collectors - Bretagne Tour

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 recude(Integer i1, Integer i2) { return i1 + i2 ; } } }) ;

La façon JDK 7

Au final, le pattern map / filter / reduce en JDK 7 :

1) Créer 3 interfaces

2) Et on applique…

Page 33: JDK 8, lambdas, streams, collectors - Bretagne Tour

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 recude(Integer i1, Integer i2) { return i1 + i2 ; } } }) ;

La façon JDK 7

Au final, le pattern map / filter / reduce en JDK 7 :

1) Créer 3 interfaces

2) Et on applique…

Page 34: JDK 8, lambdas, streams, collectors - Bretagne Tour

À la façon du JDK 8

Page 35: JDK 8, lambdas, streams, collectors - Bretagne Tour

mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { return person.getAge() ; } }

La façon du JDK 8

Prenons l’exemple du Mapper

Page 36: JDK 8, lambdas, streams, collectors - Bretagne Tour

mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } }

Prenons l’exemple du Mapper

La façon du JDK 8

Page 37: JDK 8, lambdas, streams, collectors - Bretagne Tour

mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return p.getAge() ; } }

mapper = (Person person) ;

On prend

person

La façon du JDK 8

Prenons l’exemple du Mapper

Page 38: JDK 8, lambdas, streams, collectors - Bretagne Tour

mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } }

mapper = (Person person) -> ;

et…

La façon du JDK 8

Prenons l’exemple du Mapper

Page 39: JDK 8, lambdas, streams, collectors - Bretagne Tour

mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } }

mapper = (Person person) -> person.getAge() ;

… on retourne son âge

La façon du JDK 8

Prenons l’exemple du Mapper

Page 40: JDK 8, lambdas, streams, collectors - Bretagne Tour

mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } }

mapper = (Person person) -> person.getAge() ;

La façon du JDK 8

Prenons l’exemple du Mapper

Page 41: JDK 8, lambdas, streams, collectors - Bretagne Tour

Prenons l’exemple du Mapper

Le compilateur reconnaît cette expression comme une

implémentation du mapper

mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } }

mapper = (Person person) -> person.getAge() ;

La façon du JDK 8

Page 42: JDK 8, lambdas, streams, collectors - Bretagne Tour

Que se passe-t-il si …

… il y a plus d’une ligne de code ?

Accolades, un return explicite

mapper = (Person person) -> { System.out.println("Mapping " + person) ; return person.getAge() ; }

Page 43: JDK 8, lambdas, streams, collectors - Bretagne Tour

Que se passe-t-il si …

…le type de retour est void ?

consumer = (Person person) -> p.setAge(p.getAge() + 1) ;

Page 44: JDK 8, lambdas, streams, collectors - Bretagne Tour

Que se passe-t-il si …

…la méthode prend plus d’un argument ?

Ou :

reducer = (int i1, int i2) -> { return i1 + i2 ; }

reducer = (int i1, int i2) -> i1 + i2 ;

Page 45: JDK 8, lambdas, streams, collectors - Bretagne Tour

La façon du JDK 8

Comment le compilateur reconnaît-il l’implémentation du

mapper ?

mapper = (Person person) -> person.getAge() ;

Page 46: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment le compilateur reconnaît-il l’implémentation du

mapper ?

1) Il ne faut qu’une méthode dans le mapper

mapper = (Person person) -> person.getAge() ;

La façon du JDK 8

Page 47: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment le compilateur reconnaît-il l’implémentation du

mapper ?

1) Il ne faut qu’une méthode dans le mapper

2) Les types des paramètres et le type de retour doivent

être compatible

mapper = (Person person) -> person.getAge() ;

La façon du JDK 8

Page 48: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment le compilateur reconnaît-il l’implémentation du

mapper ?

1) Il ne faut qu’une méthode dans le mapper

2) Les types des paramètres et le type de retour doivent

être compatible

3) Les exceptions jetées doivent être compatibles

mapper = (Person person) -> person.getAge() ;

La façon du JDK 8

Page 49: JDK 8, lambdas, streams, collectors - Bretagne Tour

D’autres lambdas

On peut écrire d’autres lambdas facilement :

mapper = (Person person) -> person.getAge() ; // mapper filter = (int age) -> age > 20 ; // filter reducer = (int i1, int i2) -> i1 + i2 ; // reducer

Page 50: JDK 8, lambdas, streams, collectors - Bretagne Tour

Et la plupart du temps, le compilateur reconnaît ceci :

Le type des paramètres peut être omis

mapper = person -> person.getAge() ; // mapper filter = age -> age > 20 ; // filter reducer = (i1, i2) -> i1 + i2 ; // reducer

D’autres lambdas

Page 51: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une remarque sur la réduction

Comment cela fonctionne-t-il réellement ?

Page 52: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 53: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 54: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 55: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 56: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 57: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 58: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 59: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 60: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 61: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 62: JDK 8, lambdas, streams, collectors - Bretagne Tour

Réduction

2 exemples :

Attention :

le résultat est toujours reproductible en série

il ne l’est en général pas en parallèle

Reducer r1 = (i1, i2) -> i1 + i2 ; // Ok Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // Oooops

Page 63: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pour le moment

Une expression lambda est une autre façon

d’écrire des instances de classes anonymes

Page 64: JDK 8, lambdas, streams, collectors - Bretagne Tour

Il y a d’autres syntaxes

On peut écrire :

Mais on peut aussi écrire :

mapper = person -> person.getAge() ;

mapper = Person::getAge ; // méthode non statique

Page 65: JDK 8, lambdas, streams, collectors - Bretagne Tour

Il y a d’autres syntaxes

On peut écrire :

Ou encore :

sum = (i1, i2) -> i1 + i2 ; sum = Integer::sum ; // méthode statique, nouvelle !

max = (i1, i2) -> i1 > i2 ? i1 : i2 ; max = Integer::max ; // méthode statique, nouvelle !

Page 66: JDK 8, lambdas, streams, collectors - Bretagne Tour

Il y a d’autres syntaxes

Encore un autre exemple :

toLower = String::toLowerCase ;

// !!!! NON NON NON !!!! toLowerFR = String::toLowerCase(Locale.FRANCE) ;

Page 67: JDK 8, lambdas, streams, collectors - Bretagne Tour

Plus loin sur les lambdas

Page 68: JDK 8, lambdas, streams, collectors - Bretagne Tour

Questions :

Comment modéliser une expression lambda ?

Page 69: JDK 8, lambdas, streams, collectors - Bretagne Tour

Questions :

Comment modéliser une expression lambda ?

Puis-je mettre une expression lambda dans une variable ?

Page 70: JDK 8, lambdas, streams, collectors - Bretagne Tour

Questions :

Comment modéliser une expression lambda ?

Puis-je mettre une expression lambda dans une variable ?

Un lambda est-il un objet ?

Page 71: JDK 8, lambdas, streams, collectors - Bretagne Tour

Modélisation

Un lambda = instance d’une « interface fonctionnelle »

@FunctionalInterface public interface Consumer<T> { public void accept(T t) ; }

Page 72: JDK 8, lambdas, streams, collectors - Bretagne Tour

Modélisation

Un lambda = instance d’une « interface fonctionnelle »

- ne possède qu’une unique méthode

@FunctionalInterface public interface Consumer<T> { public void accept(T t) ; }

Page 73: JDK 8, lambdas, streams, collectors - Bretagne Tour

Modélisation

Un lambda = instance d’une « interface fonctionnelle »

- ne possède qu’une unique méthode

- peut être annotée par @FunctionalInterface (optionnel)

@FunctionalInterface public interface Consumer<T> { public void accept(T t) ; }

Page 74: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

Exemple d’un consommateur :

Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ;

Consumer<String> c = s -> System.out.println(s) ;

Donc :

Page 75: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

Exemple d’un consommateur :

Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ;

Consumer<String> c = s -> System.out.println(s) ;

Question : qu’est-ce que s ?

Donc :

Page 76: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

Exemple d’un consommateur :

Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ;

Consumer<String> c = s -> System.out.println(s) ;

Réponse : le

compilateur infère qu’il

s’agit d’un String Donc :

Page 77: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

Question : peut-on écrire ce code ?

int i = ... ; Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("i = " + i) ; } } ;

Page 78: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

Question : peut-on écrire ce code ?

JDK 7 : i doit être final

int i = ... ; Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("i = " + i) ; } } ;

Page 79: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

Question : peut-on écrire ce code ?

JDK 8 : ce code compile

int i = ... ; Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("i = " + i) ; } } ;

Page 80: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

En revanche, ce code …

… ne compile pas

int i = ... ; Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("i = " + i) ; } } ; i = i + 1 ;

Page 81: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mettre un lambda dans une variable

Raison : le compilateur infère que i est effectivement final

final int i = ... ; Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("i = " + i) ; } } ; i = i + 1 ; // on ne peut pas modifier i

Page 82: JDK 8, lambdas, streams, collectors - Bretagne Tour

Note au sujet de this

JDK 7 : this est l’instance anonyme

Appeler accept() affiche donc « je suis dans c »

Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("this = " + this) ; } public String toString() { return "je suis dans c" ; } } ;

Page 83: JDK 8, lambdas, streams, collectors - Bretagne Tour

Note au sujet de this

JDK 8 : idem, accept() affiche « je suis dans c »

Mais …

Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("this = " + this) ; } public String toString() { return "je suis dans c" ; } } ;

Page 84: JDK 8, lambdas, streams, collectors - Bretagne Tour

Note au sujet de this

Un consommateur peut aussi s’écrire comme ça :

Dans ce cas, this est l’instance englobante

Consumer<String> c = s -> System.out.println(this) ;

Page 85: JDK 8, lambdas, streams, collectors - Bretagne Tour

Questions :

Quel modèle pour un lambda ?

réponse : une interface fonctionnelle

Puis-je mettre un lambda dans une variable ?

réponse : oui

Un lambda est-il un objet ?

Page 86: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un lambda est-il un objet ?

Petit jeu des 7 erreurs (avec une seule erreur)

Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ;

Consumer<String> c = s -> System.out.println(s) ;

Page 87: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un lambda est-il un objet ?

Petit jeu des 7 erreurs (avec une seule erreur)

Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s) ; } } ;

Consumer<String> c = s -> System.out.println(s) ;

Page 88: JDK 8, lambdas, streams, collectors - Bretagne Tour

Questions :

Quel modèle pour un lambda ?

réponse : une interface fonctionnelle

Puis-je mettre un lambda dans une variable ?

réponse : oui

Un lambda est-il un objet ?

réponse : non

Page 89: JDK 8, lambdas, streams, collectors - Bretagne Tour

Des lambdas à profusion :

Java.util.functions

Page 90: JDK 8, lambdas, streams, collectors - Bretagne Tour

Java.util.functions

C’est dans ce package que sont les interfaces

fonctionnelles

Il y en a 43

Page 91: JDK 8, lambdas, streams, collectors - Bretagne Tour

Java.util.functions

Supplier

Consumer / BiConsumer

Function / BiFunction (UnaryOperator / BinaryOperator)

Predicate / BiPredicate

En plus des versions construites sur les types primitifs

Page 92: JDK 8, lambdas, streams, collectors - Bretagne Tour

Supplier

Un supplier fournit un objet

public interface Supplier<T> { T get() ; }

Page 93: JDK 8, lambdas, streams, collectors - Bretagne Tour

Consumer

Un consommateur consomme un objet

public interface Consumer<T> { void accept(T t) ; }

Consumer<String> c1 = s -> System.out.println(s) ; Consumer<String> c2 = ... ; Consumer<String> c3 = c1.andThen(c2) ; persons.stream().forEach(c3) ;

Page 94: JDK 8, lambdas, streams, collectors - Bretagne Tour

BiConsumer

Prend deux arguments au lieu d’un

Peuvent être chaînés

Versions types primitifs :

- ObjIntConsumer

- ObjLongConsumer

- ObjDoubleConsumer

ObjIntConsumer prend un objet et un int en arguments

Page 95: JDK 8, lambdas, streams, collectors - Bretagne Tour

Function

Une fonction prend un objet et retourne un autre objet

Les fonctions peuvent être chaînées et / ou composées

BiFunction prend deux arguments au lieu d’un

UnaryOperator et BinaryOperator opèrent sur un seul type

public interface Function<T, R> { R apply(T t) ; }

Page 96: JDK 8, lambdas, streams, collectors - Bretagne Tour

Predicate

Un Predicate prend un objet et retourne un booléen

Il peut être inversé, et composé avec des AND ou OR

public interface Predicate<T> { boolean test(T t) ; }

Page 97: JDK 8, lambdas, streams, collectors - Bretagne Tour

BiPredicate

Un BiPredicate prend deux objets et retourne un booléen

Version pour les types booléens

public interface BiPredicate<T, U> { boolean test(T t, U u) ; }

Page 98: JDK 8, lambdas, streams, collectors - Bretagne Tour

Retour sur

map / filter / reduce

Page 99: JDK 8, lambdas, streams, collectors - Bretagne Tour

La façon JDK 7

Comment implémenter le pattern map / filter / reduce

sur List<Person> ?

Page 100: JDK 8, lambdas, streams, collectors - Bretagne Tour

La façon JDK 7

Comment implémenter le pattern map / filter / reduce

sur List<Person> ?

La façon classique est d’itérer sur les éléments et

d’appliquer le pattern

Page 101: JDK 8, lambdas, streams, collectors - Bretagne Tour

La façon JDK 7

Comment implémenter le pattern map / filter / reduce

sur List<Person> ?

La façon classique est d’itérer sur les éléments et

d’appliquer le pattern

On peut pour cela créer une méthode helper

Page 102: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mapping d’une liste

Mapping en JDK 8 avec des lambdas

List<Person> persons = new ArrayList<>() ; List<Integer> ages = Lists.map( persons, person -> person.getAge() ) ;

Page 103: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mapping d’une liste

Mapping en JDK 8 avec des lambdas

List<Person> persons = new ArrayList<>() ; List<Integer> ages = Lists.map( persons, Person::getAge ) ;

Page 104: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

Le pattern va ressembler à ça :

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 105: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

Le pattern va ressembler à ça :

L’idée est de pousser un lambda vers l’implémentation, et

de la laisser itérer en interne

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 106: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

Le pattern va ressembler à ça :

Pour : l’API peut être optimisée sans

que l’on ait à toucher au code, super !

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 107: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

Le pattern va ressembler à ça :

Pour : l’API peut être optimisée sans

que l’on ait à toucher au code, super !

Contre…

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 108: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

1) Supposons que persons soit vraiment GRANDE

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 109: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

1) Supposons que persons soit vraiment GRANDE

2 duplications : ages & agesGT20

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 110: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

1) Supposons que persons soit vraiment GRANDE

2 duplications : ages & agesGT20

Que fait-on de ces listes ? On les envoie au GC !

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 111: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

2) Supposons que les algorithmes de calcul du map / filter /

reduce soient déjà optimisés

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 112: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

2) Supposons que les algorithmes de calcul du map / filter /

reduce soient déjà optimisés

… mais on doit encore gagner du temps !

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 113: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

2) Supposons que les algorithmes de calcul du map / filter /

reduce soient déjà optimisés

Seule solution : paralléliser !

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 114: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

2) Supposons que l’on veuille paralléliser…

Il faut mieux que ages et agesGT20 soient des collections

concurrentes !

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 115: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

3) Supposons que nous aillons un reducer : allMatch

allMatch est vrai si tous les noms sont plus courts que 20

caractères

Voyons une implémentation possible de allMatch()

// pattern map / filter / reduce List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n -> n.length() < 20) ;

Page 116: JDK 8, lambdas, streams, collectors - Bretagne Tour

Écriture de allMatch()

Voici une implémentation basique de 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 ; }

Page 117: JDK 8, lambdas, streams, collectors - Bretagne Tour

Écriture de allMatch()

Voici une implémentation basique de 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 ; }

Pas besoin de parcourir

toute la liste !

Page 118: JDK 8, lambdas, streams, collectors - Bretagne Tour

Écriture de allMatch()

Voici une implémentation basique de 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 ; }

Sauf que…

Page 119: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

Quand on applique la réduction allMatch()…

… names a déjà été évaluée !

// pattern map / filter / reduce List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n.length() < 20) ;

Page 120: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

Quand on applique la réduction allMatch()…

… names a déjà été évaluée !

On a perdu une belle optimisation…

// pattern map / filter / reduce List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n.length() < 20) ;

Page 121: JDK 8, lambdas, streams, collectors - Bretagne Tour

Pattern complet

Quand on applique la réduction allMatch()…

… names a déjà été évaluée !

Il aurait fallu appliquer le mapping de façon lazy

// pattern map / filter / reduce List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n.length() < 20) ;

Page 122: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Pour : 1

Contre : 3 (au moins)

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Page 123: JDK 8, lambdas, streams, collectors - Bretagne Tour

// pattern map / filter / reduce 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, (i1, i2) -> i1 + i2) ;

Conclusion

Pour : 1

Contre : 3 (au moins)

Page 124: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Et il en va de même pour celui-ci :

// pattern map / filter / reduce List<Person> persons = ... ; List<Integer> ages = persons.map(p -> p.getAge()) ; List<Integer> agesGT20 = ages.filter(a -> a > 20) ; int sum = agesGT20.reduce(ages, (i1, i2) -> i1 + i2) ;

Page 125: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion (again)

On a besoin d’un nouveau concept pour traiter les listes de

grande taille de façon efficace

Page 126: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion (again)

On a besoin d’un nouveau concept pour traiter les listes de

grande taille de façon efficace

Le framework Collection n’apporte pas de solution

satisfaisante

Page 127: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion (again)

On a besoin d’un nouveau concept pour traiter les listes de

grande taille de façon efficace

Le framework Collection n’apporte pas de solution

satisfaisante

On a besoin de quelque chose d’autre !

Page 128: JDK 8, lambdas, streams, collectors - Bretagne Tour

Quel pattern choisir ?

Page 129: JDK 8, lambdas, streams, collectors - Bretagne Tour

Introduction

Implémenter le map / filter / reduce sur Collection aurait

mené à ceci :

Et même si cette approche n’est pas viable, c’est une

façon agréable d’écrire les choses

// map / filter / reduce pattern sur Collection int sum = persons .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

Page 130: JDK 8, lambdas, streams, collectors - Bretagne Tour

Introduction

Conservons le même genre de pattern, en ajoutant un

appel intermédiaire

// map / filter / reduce pattern sur Collection int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

Page 131: JDK 8, lambdas, streams, collectors - Bretagne Tour

Introduction

Collection.stream() retourne un Stream : une nouvelle

interface

Nouvelle interface = on a les mains libres !

// map / filter / reduce pattern sur Collection int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

Page 132: JDK 8, lambdas, streams, collectors - Bretagne Tour

Nouvelle interface Collection

Donc on a besoin d’une nouvelle méthode sur Collection

public interface Collection<E> { // nos bonnes vieilles méthodes Stream<E> stream() ; }

Page 133: JDK 8, lambdas, streams, collectors - Bretagne Tour

Nouvelle interface Collection

Problème : ArrayList ne compile plus…

Une solution doit être trouvée !

public interface Collection<E> { // nos bonnes vieilles méthodes Stream<E> stream() ; }

Page 134: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces

Problème : ArrayList ne compile plus…

Une solution doit être trouvée !

Une solution qui ne nécessite pas de modifier ou de

recompiler toutes les implémentations existantes de

Collection !

Page 135: JDK 8, lambdas, streams, collectors - Bretagne Tour

Nouvelles interfaces

Page 136: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces Java 8

Problème : ArrayList ne compile plus…

Une solution doit être trouvée !

Une solution qui ne nécessite pas de modifier ou de

recompiler toutes les implémentations existantes de

Collection !

Problème : ajouter des méthodes à une interface sans

toucher aux implémentations…

Page 137: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème : ajouter des méthodes à une interface sans

toucher aux implémentations…

Solution : changer la façon dont les interfaces fonctionnent

en Java !

Interfaces Java 8

Page 138: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces Java 8

ArrayList a besoin de l’implémentation de stream()…

public interface Collection<E> { // nos bonnes vieilles méthodes Stream<E> stream() ; }

Page 139: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces Java 8

Solution : mettons-les dans l’interface !

public interface Collection<E> { // nos bonnes vieilles méthodes default Stream<E> stream() { return ... ; } }

Page 140: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces Java 8

Introduisons les default methods en Java

Les méthodes par défaut permettent de faire évoluer de

vieilles interfaces

public interface Collection<E> { // nos bonnes vieilles méthodes default Stream<E> stream() { return ... ; } }

Page 141: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Cela amène-t-il l’héritage multiple en Java ?

Page 142: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Cela amène-t-il l’héritage multiple en Java ?

Oui, mais on l’a déjà

Page 143: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Cela amène-t-il l’héritage mutliple en Java ?

Oui, mais on l’a déjà

public class String implements Serializable, Comparable<String>, CharSequence { // ... }

Page 144: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Ce que l’on a en Java est l’héritage multiple de type

Page 145: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Ce que l’on a en Java est l’héritage multiple de type

Java 8 amène l’héritage multiple d’implémentation

Page 146: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Ce que l’on a en Java est l’héritage multiple de type

Java 8 amène l’héritage multiple d’implémentation

Ce que l’on a pas, c’est l’héritage multiple d’état

Page 147: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Ce que l’on a en Java est l’héritage multiple de type

Java 8 amène l’héritage multiple d’implémentation

Ce que l’on a pas, c’est l’héritage multiple d’état

… et d’ailleurs, on n’en veut pas !

Page 148: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Peut-on avoir des conflits ?

Page 149: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Peut-on avoir des conflits ?

Hélas oui…

Page 150: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Peut-on avoir des conflits ?

Hélas oui…

Il nous faut donc des règles pour les gérer

Page 151: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

public class C implements A, B { // ... }

Page 152: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

public class C implements A, B { // ... }

public interface A { default String a() { ... } }

public interface B { default String a() { ... } }

Page 153: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Exemple #0

La classe gagne !

L’implémentation efface la méthode par défaut

public class C implements A, B { public String a() { ... } }

public interface A { default String a() { ... } }

public interface B { default String a() { ... } }

Page 154: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Exemple #1

public class C implements A, B { // ... }

public interface A { default String a() { ... } }

public interface B { default String a() { ... } }

Page 155: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Exemple #1

Erreur de compilation :

class C inherits unrelated defaults for a() from types A and B

public class C implements A, B { // ... }

public interface A { default String a() { ... } }

public interface B { default String a() { ... } }

Page 156: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Exemple #2

A est plus spécifique que B : A.a() a la priorité

public class C implements A, B { // ... }

public interface A extends B { default String a() { ... } }

public interface B { default String a() { ... } }

Page 157: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Exemple #2

public class C implements A { // ... }

public interface A extends B { default String a() { ... } }

public interface B { default String a() { ... } }

public class D extends C implements B { // ... }

Page 158: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Exemple #2

A est toujours plus spécifique que B : A.a() gagne

public class C implements A { // ... }

public interface A extends B { default String a() { ... } }

public interface B { default String a() { ... } }

public class D extends C implements B { // ... }

Page 159: JDK 8, lambdas, streams, collectors - Bretagne Tour

Méthodes par défaut

Retour sur l’exemple #1

On peut aussi faire un appel explicite

public interface A { default String a() { ... } }

public interface B { default String a() { ... } }

public class C implements A, B { public String a() { return B.super.a() ; } }

Page 160: JDK 8, lambdas, streams, collectors - Bretagne Tour

2 règles simples

Pour gérer les conflits de l’héritage multiple d’implémentation

Page 161: JDK 8, lambdas, streams, collectors - Bretagne Tour

2 règles simples

Pour gérer les conflits de l’héritage multiple d’implémentation

1) La classe gagne

Page 162: JDK 8, lambdas, streams, collectors - Bretagne Tour

2 règles simples

Pour gérer les conflits de l’héritage multiple d’implémentation

1) La classe gagne

2) Les implémentations les plus spécifiques gagnent

Page 163: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un exemple

On a tous écrit une implémentation d’Iterator :

public class MyIterator implements Iterator<E> { // du code métier super important public void remove() { throw new UnsupportedOperationException("Naaaaaaan !") ; } ; }

Page 164: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un exemple

Grâce à Java 8 :

Plus besoin d’écrire ce code !

public interface Iterator<E> { default void remove() { throw new UnsupportedOperationException("remove") ; } ; }

Page 165: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces en Java 8

Donc on change le concept d’interfaces en Java 8

public class HelloWorld { // souvenir souvenir public static void main(String[] args) { System.out.println("Hello world!") ; } ; }

Page 166: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces en Java 8

Donc on change le concept d’interfaces en Java 8

Vraiment…

public interface HelloWorld { // souvenir souvenir public static void main(String[] args) { System.out.println("Hello world!") ; } ; }

Page 167: JDK 8, lambdas, streams, collectors - Bretagne Tour

Interfaces en Java 8

1) Méthodes par défaut, héritage multiple

d’implémentation

règles pour gérer les conflits, qui s’appliquent à la compilation

2) Des méthodes statiques dans les interfaces

Tout ceci va permettre de nouvelles manières

d’écrire les APIs

Page 168: JDK 8, lambdas, streams, collectors - Bretagne Tour

On en sommes-nous ?

1) On a une nouvelle syntaxe : les lambdas

2) On a un pattern à implémenter

3) On a des nouvelles interfaces qui permettent de

conserver la compatibilité ascendante

Page 169: JDK 8, lambdas, streams, collectors - Bretagne Tour

L’API Stream

Page 170: JDK 8, lambdas, streams, collectors - Bretagne Tour

Qu’est-ce qu’un Stream ?

D’un point de vue technique : une interface paramétrée

« un stream de String »

Page 171: JDK 8, lambdas, streams, collectors - Bretagne Tour

Qu’est-ce qu’un Stream ?

D’un point de vue technique : une interface paramétrée

D’autres interfaces pour les types primitifs :

« un stream de String »

IntStream, LongStream, DoubleStream

Page 172: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une nouvelle notion

Cela ressemble à une collection, mais…

- Un Stream est construit sur une « source »

- Pas de donnée à la construction

- Pas de limite sur ce qu’une source peut produire

Page 173: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une nouvelle notion

Un Stream peut être construit sur :

- Une collection ou un tableau

- Un itérateur

- Un source I/O

Certaines de ces sources sont « infinies »

Page 174: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une nouvelle notion

1) Un stream ne porte pas de donnée

Il s’agit juste d’un objet sur lequel on peut déclarer des

opérations

Page 175: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une nouvelle notion

1) Un stream ne porte pas de donnée

2) Un stream ne peut pas modifier sa source

Conséquence : il peut mener ses traitements en parallèle

Page 176: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une nouvelle notion

1) Un stream ne porte pas de donnée

2) Un stream ne peut pas modifier sa source

3) Une source peut être infinie, ou non bornée

On a donc besoin de garantir que les calculs seront menés

en temps fini

Page 177: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une nouvelle notion

1) Un stream ne porte pas de donnée

2) Un stream ne peut pas modifier sa source

3) Une source peut être infinie, ou non bornée

4) Un Stream traite ses données de façon lazy

On peut optimiser les opérations entre elles

On a besoin d’un mécanisme pour déclencher les

traitements

Page 178: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream est-il une Collection ?

La réponse est non

Page 179: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream est-il une Collection ?

La réponse est non

Une collection permet d’itérer sur ses éléments

Page 180: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream est-il une Collection ?

La réponse est non

Une collection permet d’itérer sur ses éléments

Pas un Stream

Page 181: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream est-il une Collection ?

La réponse est non

Une collection ne permet pas d’appliquer un lambda sur

ses éléments

Page 182: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream est-il une Collection ?

La réponse est non

Une collection ne permet pas d’appliquer un lambda sur

ses éléments

Un Stream le peut

Page 183: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment construire un Stream ?

De nombreuses façons de faire…

1) À partir d’une collection :

Collection<String> collection = ... ; Stream<String> stream = collection.stream() ;

Page 184: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment construire un Stream ?

De nombreuses façons de faire…

1) À partir d’une collection :

2) À partir d’un tableau :

Stream<String> stream2 = Arrays.stream(new String [] {"one", "two", "three"}) ;

Page 185: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment construire un Stream ?

De nombreuses façons de faire…

1) À partir d’une collection :

2) À partir d’un tableau :

3) À partir des méthodes factory de Stream

Stream<String> stream1 = Stream.of("one", "two", "three") ;

Page 186: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment construire un Stream ?

Encore quelques patterns :

Stream.empty() ; // Stream vide Stream.of(T t) ; // un seul élément Stream.generate(Supplier<T> s) ; Stream.iterate(T seed, UnaryOperator<T> f) ;

Page 187: JDK 8, lambdas, streams, collectors - Bretagne Tour

Comment construire un Stream ?

Encore d’autres façons :

string.chars() ; // retourne un IntStream lineNumberReader.lines() ; // retourne un Stream<String> random.ints() ; // retourne un IntStream

Page 188: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un premier exemple

Retour sur notre map / filter / reduce

// map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

Construction d’un

Stream sur une List

Page 189: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un premier exemple

Retour sur notre map / filter / reduce

// map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

Déclarations

Page 190: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un premier exemple

Retour sur notre map / filter / reduce

// map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

On lance le

calcul

Page 191: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un premier exemple

Retour sur notre map / filter / reduce

// map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

Valeur par défaut, dans le

cas d’un stream vide

Page 192: JDK 8, lambdas, streams, collectors - Bretagne Tour

Deux types d’opérations

On peut donc déclarer des opérations sur un Stream

Deux types d’opérations :

1) Les opérations intermédiaires

Exemple : map, filter

Page 193: JDK 8, lambdas, streams, collectors - Bretagne Tour

Deux types d’opérations

On peut donc déclarer des opérations sur un Stream

Deux types d’opérations :

1) Les opérations intermédiaires

Exemple : map, filter

2) Les opérations terminales, qui déclenchent le traitement

Exemple : reduce

Page 194: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream possède un état

Les implémentations de Stream possèdent un état :

- SIZED = le cardinal du Stream est connu

- ORDERED = l’ordre du Stream est important (List)

- DISTINCT = pas de doublon dans le Stream (Set)

- SORTED = les Stream est trié (SortedSet)

Page 195: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream possède un état

Certaines opérations changent cet état

- le filtrage annule SIZED

- le mapping annule DISTINCT et SORTED

Page 196: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream possède un état

Et cela permet d’optimiser !

- un HashSet ne peut pas avoir de doublons…

Page 197: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream possède un état

Et cela permet d’optimiser !

- un HashSet ne peut pas avoir de doublons…

- donc distinct() ne fait rien (NOP) pour un stream

construit sur un HashSet

Page 198: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream possède un état

Et cela permet d’optimiser !

- un HashSet ne peut pas avoir de doublons…

- donc distinct() ne fait rien (NOP) pour un stream

construit sur un HashSet

- un TreeSet est trié, donc…

Page 199: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un Stream possède un état

Et cela permet d’optimiser !

- un HashSet ne peut pas avoir de doublons…

- donc distinct() ne fait rien (NOP) pour un stream

construit sur un HashSet

- un TreeSet est trié, donc…

- sort() ne fait rien pour un stream construit sur un

TreeSet

Page 200: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un 2ème exemple

Trions une liste de chaîne de caractères

List<String> strings = new ArrayList<>() ; // on remplit la liste // et plus loin dans le code on a ça : List<String> result = strings.stream() .sorted() .collect(Collector.toList()) ;

Page 201: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un 2ème exemple

Trions une liste de chaîne de caractères

// un jour un petit malin change l’implémentation SortedSet<String> strings = new TreeSet<>() ; // on remplit la liste // et plus loin dans le code on a ça : List<String> result = strings.stream() .sorted() .collect(Collector.toList()) ;

Page 202: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un 2ème exemple

Trions une liste de chaîne de caractères

Dans ce cas le code de tri n’est plus exécuté !

// un jour un petit malin change l’implémentation SortedSet<String> strings = new TreeSet<>() ; // on remplit la liste // et plus loin dans le code on a ça : List<String> result = strings.stream() .sorted() .collect(Collector.toList()) ;

Page 203: JDK 8, lambdas, streams, collectors - Bretagne Tour

Opérations stateless / statefull

Opérations Stateless / statefull

Certaines opérations sont stateless :

Cela signifie que l’on n’a pas besoin de plus d’information

que ce qui est contenu dans p

persons.stream().map(p -> p.getAge()) ;

Page 204: JDK 8, lambdas, streams, collectors - Bretagne Tour

Opérations stateless / statefull

Opérations Stateless / statefull

Certaines opérations sont stateless :

Pas le cas de toutes les opérations

persons.stream().map(p -> p.getAge()) ;

stream.limit(10_000_000) ; // select les premiers 10M éléments

Page 205: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de 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()) ; }

Page 206: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de 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()) ; }

Page 207: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de String

Meilleur ?

Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(rand.nextLong()) ) ;

Page 208: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de String

Meilleur !

// Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(ThreadLocalRandom.current().nextLong()) ) ;

Page 209: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de String

// other way Stream<String> stream = ThreadLocalRandom .current() .longs() // returns a LongStream .mapToObj(l -> Long.toHexString(l)) ;

Page 210: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de String

// other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) ;

Page 211: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de String

// other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ;

T = 4 ms

Page 212: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de String

// other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ;

Page 213: JDK 8, lambdas, streams, collectors - Bretagne Tour

Exemple

Trions un tableau de String

// other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ;

T = 14 s

Page 214: JDK 8, lambdas, streams, collectors - Bretagne Tour

Example

Trions un tableau de String

// other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ;

T = 14 s

Opérations

intermédiares !

Page 215: JDK 8, lambdas, streams, collectors - Bretagne Tour

Au final

1) Il y a des opérations intermédiaires, et des opérations

terminales

2) Seule une opération terminale déclenche les

traitements

Page 216: JDK 8, lambdas, streams, collectors - Bretagne Tour

Au final

1) Il y a des opérations intermédiaires, et des opérations

terminales

2) Seule une opération terminale déclenche les

traitements

3) Une seule opération terminale est autorisée

4) Un Stream ne peut traité qu’une seule fois

Si besoin, un autre Stream doit être construit

Page 217: JDK 8, lambdas, streams, collectors - Bretagne Tour

Parallel Streams

Page 218: JDK 8, lambdas, streams, collectors - Bretagne Tour

Optimisation

La première optimisation (après l’exécution lazy) est le

parallélisme

Fork / join permet la programmation parallèle depuis le

JDK 7

Écrire du code parallèle reste complexe, et ne mène pas

toujours à de meilleures performances

Page 219: JDK 8, lambdas, streams, collectors - Bretagne Tour

Optimisation

La première optimisation (après l’exécution lazy) est le

parallélisme

Fork / join permet la programmation parallèle depuis le

JDK 7

Écrire du code parallèle reste complexe, et ne mène pas

toujours à de meilleures performances

Utiliser l’API Stream est plus simple et plus sûr

Page 220: JDK 8, lambdas, streams, collectors - Bretagne Tour

Construction d’un Stream parallèle

Deux patterns

1) Appeler parallelStream() au lieu de stream()

2) Appeler parallel() sur un stream existant

Stream<String> s = strings.parallelStream() ;

Stream<String> s = strings.stream().parallel() ;

Page 221: JDK 8, lambdas, streams, collectors - Bretagne Tour

Peut-on choisir le nombre cœurs ?

La réponse est non

Page 222: JDK 8, lambdas, streams, collectors - Bretagne Tour

Peut-on choisir le nombre cœurs ?

La réponse est non

Mais… en a-t-on vraiment besoin ?

Page 223: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

En fait oui !

Page 224: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

En fait oui !

… et non…

Page 225: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 :

« retourne les premiers 10M éléments »

persons.stream().limit(10_000_000) ;

Page 226: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 :

« retourne les premiers 10M éléments »

persons.parallelStream().limit(10_000_000) ;

Page 227: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 :

« retourne les premiers 10M éléments »

Comment peut-on tracer que les éléments sont les

premiers sur un CPU multicœur ?

persons.parallelStream().limit(10_000_000) ;

Page 228: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 : performances

Code 1

List<Long> list = new ArrayList<>(10_000_100) ; for (int i = 0 ; i < 10_000_000 ; i++) { list1.add(ThreadLocalRandom.current().nextLong()) ; }

Page 229: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 : performances

Code 2

Stream<Long> stream = Stream.generate(() -> ThreadLocalRandom.current().nextLong()) ; List<Long> list1 = stream.limit(10_000_000).collect(Collectors.toList()) ;

Page 230: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 : performances

Code 3

Stream<Long> stream = ThreadLocalRandom.current().longs(10_000_000).mapToObj(Long::new) ; List<Long> list = stream.collect(Collectors.toList()) ;

Page 231: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 : performances

Série Parallèle

Code 1 (for) 270 ms

Code 2 (limit) 310 ms

Code 3 (longs) 250 ms

Page 232: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 1 : performances

Série Parallèle

Code 1 (for) 270 ms

Code 2 (limit) 310 ms 500 ms

Code 3 (longs) 250 ms 320 ms

Page 233: JDK 8, lambdas, streams, collectors - Bretagne Tour

Paralléliser est-il aussi simple ?

Exemple 2 :

« retourne un Stream composé des éléments du premier

Stream, puis des éléments du second »

Stream s3 = Stream.concat(stream1, stream2) ;

Page 234: JDK 8, lambdas, streams, collectors - Bretagne Tour

Parallélisme

Le parallélisme implique des calculs supplémentaires la

plupart du temps

Des opérations mal configurées vont entraîner des calculs

inutiles, qui vont affaiblir les performances globales

Exemple : un stream ORDERED est plus complexe à

traiter

Page 235: JDK 8, lambdas, streams, collectors - Bretagne Tour

Réductions

Page 236: JDK 8, lambdas, streams, collectors - Bretagne Tour

Qu’est-ce qu’une réduction ?

2 types de réduction :

1) La réduction

« A reduction operation (also called a fold) takes a

sequence of input elements and combines them

into a single summary result by repeated

application of a combining operation »

Page 237: JDK 8, lambdas, streams, collectors - Bretagne Tour

Qu’est-ce qu’une réduction ?

2 types de réduction :

2) La réduction mutable

« A mutable reduction operation accumulates

input elements into a mutable result container,

such as a Collection or StringBuilder, as it

processes the elements in the stream »

Page 238: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une réduction simple

La somme des âges

// map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;

Page 239: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une réduction simple

Ca serait bien de pouvoir écrire :

Mais sum() n’est pas définie sur Stream<T>

Que voudrait dire « additionner des personnes » ?

// map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ;

Page 240: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une réduction simple

Ca serait bien de pouvoir écrire :

Mais sum() n’est pas définie sur Stream<T>

Mais il y a une méthode sum() sur IntStream !

// map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ;

Page 241: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une réduction simple

2ème version :

Résultat pour une liste vide : 0

// map / filter / reduce pattern on collections int sum = persons.stream() .map(Person::getAge) .filter(a -> a > 20) .mapToInt(Integer::intValue) .sum() ;

Page 242: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une réduction simple

2ème version (encore meilleure) :

Résultat pour une liste vide : 0

// map / filter / reduce pattern on collections int sum = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) // .mapToInt(Integer::intValue) .sum() ;

Page 243: JDK 8, lambdas, streams, collectors - Bretagne Tour

Une réduction simple

Qu’en est-il de min() et de max() ?

Quelle valeur par défaut pour max() ?

// map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;

Page 244: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

La notion de « valeur par défaut » est plus complexe qu’il y

paraît…

Page 245: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

La notion de « valeur par défaut » est plus complexe qu’il y

paraît…

1) La « valeur par défaut » est la réduction de l’ensemble

vide

Page 246: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

La notion de « valeur par défaut » est plus complexe qu’il y

paraît…

1) La « valeur par défaut » est la réduction de l’ensemble

vide

2) Mais aussi l’élément neutre de la réduction

Page 247: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 248: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 249: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 250: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 251: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 252: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 253: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 254: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 255: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

La notion de « valeur par défaut » est plus complexe qu’il y

paraît…

1) La « valeur par défaut » est la réduction de l’ensemble

vide

2) Mais aussi l’élément neutre de la réduction

Page 256: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

Problème : max() and min() n’ont pas d’élément neutre

ie : un élément e pour lequel max(e, a) = a

Page 257: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

Problème : max() and min() n’ont pas d’élément neutre

ie : un élément e pour lequel max(e, a) = a

Ca ne peut pas être 0 : max(-1, 0) = 0

−∞ n’est pas un entier

Page 258: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

Donc quelle est la valeur par défaut de max() et min() ?

Page 259: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

Donc quelle est la valeur par défaut de max() et min() ?

Réponse : il n’y a pas de valeur par défaut pour max() et

pour min()

Page 260: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

Donc quel type choisir pour la méthode max() ?

// map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;

Page 261: JDK 8, lambdas, streams, collectors - Bretagne Tour

Problème des valeurs par défaut

Donc quel type choisir pour la méthode max() ?

Si c’est int, la valeur par défaut sera 0…

// map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;

Page 262: JDK 8, lambdas, streams, collectors - Bretagne Tour

Optionals

Sans valeur par défaut, il faut trouver autre chose…

// map / filter / reduce pattern on collections OptionalInt optionalMax = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;

« il peut ne pas y avoir

de résultat »

Page 263: JDK 8, lambdas, streams, collectors - Bretagne Tour

Optionals

Que peut-on faire avec OptionalInt ?

1er pattern : tester s’il contient une valeur

OptionalInt optionalMax = ... ; int max ; if (optionalMax.isPresent()) { max = optionalMax.get() ; } else { max = ... ; // décider d’une « valeur par défaut » }

Page 264: JDK 8, lambdas, streams, collectors - Bretagne Tour

Optionals

Que peut-on faire avec OptionalInt ?

2ème pattern : lire la valeur ou jeter une exception

OptionalInt optionalMax = ... ; // throws NoSuchElementException if no held value int max = optionalMax.getAsInt() ;

Page 265: JDK 8, lambdas, streams, collectors - Bretagne Tour

Optionals

Que peut-on faire avec OptionalInt ?

3éme pattern : lire la valeur / retourner une valeur par défaut

OptionalInt optionalMax = ... ; // get 0 if no held value int max = optionalMax.orElse(0) ;

Page 266: JDK 8, lambdas, streams, collectors - Bretagne Tour

Optionals

Que peut-on faire avec OptionalInt ?

4ème pattern : lire la valeur ou jeter une exception

OptionalInt optionalMax = ... ; // exceptionSupplier will supply an exception, if no held value int max = optionalMax.orElseThrow(exceptionSupplier) ;

Page 267: JDK 8, lambdas, streams, collectors - Bretagne Tour

Available Optionals

Optional<T>

OptionalInt, OptionalLong, OptionalDouble

Page 268: JDK 8, lambdas, streams, collectors - Bretagne Tour

Réductions disponibles

Sur Stream<T> :

- reduce()

- count(), min(), max()

- anyMatch(), allMatch(), noneMatch()

- findFirst(), findAny()

- toArray()

- forEach(), forEachOrdered()

Page 269: JDK 8, lambdas, streams, collectors - Bretagne Tour

Réductions disponibles

Sur IntStream, LongStream, DoubleStream :

- average()

- summaryStatistics()

Page 270: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions : exemple 1

Utilisation d’une classe helper : Collectors

ArrayList<String> strings = stream .map(Object::toString) .collect(Collectors.toList()) ;

Page 271: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions : exemple 2

Concaténation de String avec un helper

String names = persons .stream() .map(Person::getName) .collect(Collectors.joining()) ;

Page 272: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions

Une réduction mutable dépend :

- d’un container : Collection or StringBuilder

- d’un moyen d’ajouter un élément au container

- d’un moyen de fusionner deux containers (utilisé dans

les opérations parallèles)

Page 273: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions

La forme générale a donc besoin de 3 objets :

- un supplier : construit une instance du container

Supplier<ArrayList<String>> supplier = () -> new ArrayList<String>() ;

Page 274: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions

La forme générale a donc besoin de 3 objets :

- un supplier : construit une instance du container

- un accumulateur : ajoute un élément au container

BiConsumer<ArrayList<String>, ? super String> accumulator = (suppliedList, s) -> suppliedList.add(s) ;

Page 275: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions

La forme générale a donc besoin de 3 objets :

- un supplier : construit une instance du container

- un accumulateur : ajoute un élément au container

- un combiner : fusionne deux containers

BiConsumer<ArrayList<String>, ArrayList<String>> combiner = (supplied1, supplied2) -> supplied1.addAll(supplied2) ;

Page 276: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions : exemple 1

D’où le code :

ArrayList<String> strings = stream .map(Object::toString) .collect( () -> new ArrayList<String>(), // the supplier (suppliedList, s) -> suppliedList.add(s), // the accumulator (supplied1, supplied2) -> supplied1.addAll(supplied2) // the combiner ) ;

Page 277: JDK 8, lambdas, streams, collectors - Bretagne Tour

Mutable reductions : exemple 1

D’où le code :

ArrayList<String> strings = stream .map(Object::toString) .collect( ArrayList::new, // the supplier ArrayList::add, // the accumulator ArrayList::addAll // the combiner ) ;

Page 278: JDK 8, lambdas, streams, collectors - Bretagne Tour

Collectors

Page 279: JDK 8, lambdas, streams, collectors - Bretagne Tour

La classe Collectors

Une boite à outils (37 méthodes) pour la plupart des types

de réduction

- counting, minBy, maxBy

- summing, averaging, summarizing

- joining

- toList, toSet

Et

- mapping, groupingBy, partionningBy

Page 280: JDK 8, lambdas, streams, collectors - Bretagne Tour

La classe Collectors

Average, Sum, Count

persons .stream() .collect(Collectors.averagingDouble(Person::getAge)) ;

persons .stream() .collect(Collectors.counting()) ;

Page 281: JDK 8, lambdas, streams, collectors - Bretagne Tour

La classe Collectors

Concaténation des nom dans une String

String names = persons .stream() .map(Person::getName) .collect(Collectors.joining(", ")) ;

Page 282: JDK 8, lambdas, streams, collectors - Bretagne Tour

La classe Collectors

Accumulation dans un Set

Set<Person> setOfPersons = persons .stream() .collect( Collectors.toSet()) ;

Page 283: JDK 8, lambdas, streams, collectors - Bretagne Tour

La classe Collectors

Accumulation dans une collection passée en paramètre

TreeSet<Person> treeSetOfPersons = persons .stream() .collect( Collectors.toCollection(TreeSet::new)) ;

Page 284: JDK 8, lambdas, streams, collectors - Bretagne Tour

La classe Collectors

Calculer un max avec un comparateur

Bonus : une API Comparator

Optional<Person> optionalPerson = persons .stream() .collect( Collectors.maxBy( Comparator.comparing(Person::getAge)) ;

Page 285: JDK 8, lambdas, streams, collectors - Bretagne Tour

Construction de comparateurs

Nouvelle API pour construire des comparateurs

Comparator<Person> comp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person:getAge) ;

Page 286: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : mapping

La méthode mapping() prend 2 paramètres

- une fonction, qui mappe les éléments du Stream

- un collecteur, appelé « downstream », appliqué aux

valeurs mappées

Page 287: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : mapping

Accumuler les noms des personnes dans un Set

Set<String> set = persons .stream() .collect( Collectors.mapping( Person::getLastName, Collectors.toSet())) ;

Page 288: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : mapping

Mapper le stream, accumulation dans une collection

TreeSet<String> set = persons .stream() .collect( Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new)) ) ;

Page 289: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : groupingBy

« Grouping by » construit des tables de hachage

- méthode de construction des clés

- par défaut les éléments sont rangés dans une liste

- on peut spécifier un downstream (collector)

Page 290: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : groupingBy

Des personnes par âge

Map<Integer, List<Person>> map = persons .stream() .collect( Collectors.groupingBy(Person::getAge)) ;

Page 291: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : groupingBy

… rangées dans des Set

Map<Integer, Set<Person>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.toSet() // le downstream ) ;

Page 292: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : groupingBy

… on ne garde que les noms

Map<Integer, Set<String>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.mapping( // Person::getLastName, // the downstream Collectors.toSet() // ) ) ;

Page 293: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : groupingBy

… les noms rangés dans des TreeSet

Map<Integer, TreeSet<String>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new) ) ) ;

Page 294: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : groupingBy

… etc… etc… etc…

TreeMap<Integer, TreeSet<String>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, TreeMap::new, Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new) ) ) ;

Page 295: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : groupingBy

Exemple : création d’un histogramme des âges

Donne le nombre de personnes par âge

Map<Integer, Long> map = persons .stream() .collect( Collectors.groupingBy(Person::getAge, Collectors.counting()) ) ;

Page 296: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : partionningBy

Crée une Map<Boolean, …> à partir d’un prédicat

- la table a deux clés : TRUE et FALSE

- et on peut y ajouter un downstream

Page 297: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : partionningBy

Crée une Map<Boolean, …> avec un prédicat

map.get(TRUE) retourne la liste des personnes de plus de

20 ans

Map<Boolean, List<Person>> map = persons .stream() .collect( Collectors.partitioningBy(p -> p.getAge() > 20) ) ;

Page 298: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : partionningBy

On peut définir d’autres traitements

Map<Boolean, TreeSet<String>> map = persons .stream() .collect( Collectors.partitioningBy( p -> p.getAge() > 20, Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new)) ) ) ) ;

Page 299: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : collectingAndThen

Collecte les données avec un downstream

Applique enfin une fonction appelée « finisher »

Indispensable pour retourner des collections immutables

Page 300: JDK 8, lambdas, streams, collectors - Bretagne Tour

Classe Collectors : collectingAndThen

Dans ce cas « Map::entrySet » est un finisher

Set<Map.Entry<Integer, List<Person>>> set = persons .stream() .collect( Collectors.collectingAndThen( Collectors.groupingBy( Person::getAge), // downstream, construit une map Map::entrySet // finisher, appliqué à la map ) ;

Page 301: JDK 8, lambdas, streams, collectors - Bretagne Tour

Quelques exemples réels

Page 302: JDK 8, lambdas, streams, collectors - Bretagne Tour

1er exemple

Optional<Entry<Integer, Long>> opt = movies.stream().parallel() .collect( Collectors.collectingAndThen( Collectors.groupingBy( movie -> movie.releaseYear(), Collectors.counting() ), Map::entrySet ) ) .stream() .max(Map.Entry.comparingByValue()) ;

Page 303: JDK 8, lambdas, streams, collectors - Bretagne Tour

1er exemple

Optional<Entry<Integer, Long>> opt = movies.stream().parallel() .collect( Collectors.collectingAndThen( Collectors.groupingBy( movie -> movie.releaseYear(), Collectors.counting() ), Map::entrySet ) ) .stream() .max(Map.Entry.comparingByValue()) ;

Un stream de movies

Page 304: JDK 8, lambdas, streams, collectors - Bretagne Tour

1er exemple

Optional<Entry<Integer, Long>> opt = movies.stream().parallel() .collect( Collectors.collectingAndThen( Collectors.groupingBy( movie -> movie.releaseYear(), Collectors.counting() ), Map::entrySet ) ) .stream() .max(Map.Entry.comparingByValue()) ;

Construction d’une map année / # de films

Page 305: JDK 8, lambdas, streams, collectors - Bretagne Tour

1er exemple

Optional<Entry<Integer, Long>> opt = movies.stream().parallel() .collect( Collectors.collectingAndThen( Collectors.groupingBy( movie -> movie.releaseYear(), Collectors.counting() ), Map::entrySet ) ) .stream() .max(Map.Entry.comparingByValue()) ;

Construction de l’EntrySet

Page 306: JDK 8, lambdas, streams, collectors - Bretagne Tour

1er exemple

Optional<Entry<Integer, Long>> opt = movies.stream().parallel() .collect( Collectors.collectingAndThen( Collectors.groupingBy( movie -> movie.releaseYear(), Collectors.counting() ), Map::entrySet ) ) .stream() .max(Map.Entry.comparingByValue()) ;

Et déterminer la plus grande valeur

Page 307: JDK 8, lambdas, streams, collectors - Bretagne Tour

1er exemple

Optional<Entry<Integer, Long>> opt = movies.stream().parallel() .collect( Collectors.collectingAndThen( Collectors.groupingBy( movie -> movie.releaseYear(), Collectors.counting() ), Map::entrySet ) ) .stream() .max(Map.Entry.comparingByValue()) ;

Retourne l’année qui a vu le plus grand nombre de films

Page 308: JDK 8, lambdas, streams, collectors - Bretagne Tour

Remarques sur parallel()

Page 309: JDK 8, lambdas, streams, collectors - Bretagne Tour

Traitement parallèle

Deux façons de le faire :

- créer un stream parallèle d’entrée

- Appeler parallel() sur un stream donné, on peut aussi

appeler sequential()

Quand une opération terminale est appelée, l’ensemble

des opérations est exécuté en parallèle ou pas, en fonction

du mode du stream

Page 310: JDK 8, lambdas, streams, collectors - Bretagne Tour

Les choses qui ne marchent pas

Certaines opérations ne donnent pas des résultats

reproductibles en parallèle, ex : findAny()

On ne doit pas modifier la source durant le traitement, si on

le fait les résultats ne sont pas prévisibles, même si la

source est une collection concurrente

Page 311: JDK 8, lambdas, streams, collectors - Bretagne Tour

Les choses qui marchent pas, mais…

Le traitement parallèle doit être le moins contraint possible

pour être efficace

- ORDERED est une contrainte coûteuse

- collect() doit utiliser des structures concurrentes

Page 312: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Page 313: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Pourquoi les lambdas ont-ils été introduits dans Java 8 ?

Page 314: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Pourquoi les lambdas ont-ils été introduits dans Java 8 ?

Réponse : parce que c’est à la mode !

Page 315: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 316: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Pourquoi les lambdas ont-ils été introduits dans Java 8 ?

Réponse : parce que c’est à la mode !

Page 317: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Pourquoi les lambdas ont-ils été introduits dans Java 8 ?

Réponse : parce que c’est à la mode !

Parce que le code écrit est plus compact !

Page 318: JDK 8, lambdas, streams, collectors - Bretagne Tour

Plus c’est court, plus c’est bon !

Un exemple de code compact

#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) ; }

http://www.codeproject.com/Articles/2228/Obfuscating-your-Mandelbrot-code

Page 319: JDK 8, lambdas, streams, collectors - Bretagne Tour

Un exemple de code compact

#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) ; }

http://www.codeproject.com/Articles/2228/Obfuscating-your-Mandelbrot-code

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++ ++ +++++++++++ +++++++++++++++++++++++++++++++++++ +++++++ ++++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++++ ++++++++ ++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++ +++++ +++++++++++++++++++++++++++++++++ ++++++ +++++++++++++++++++++++ + +++++ ++++++ +++++++++++++++++++++++ ++ ++++++ ++++++++++++++++++++++ + ++++++ ++++++++++++++++++++++ + ++++++ ++++++++++++++++++++ + + +++++++ ++++++ ++++++++ ++++++++++++++++++++ + + +++++++ ++++++++++++++++++++++ + ++++++ ++++++++++++++++++++++ + ++++++ +++++++++++++++++++++++ ++ ++++++ +++++++++++++++++++++++ + +++++ ++++++ +++++++++++++++++++++++++++++++++ ++++++ +++++++++++++++++++++++++++++++++ +++++ ++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++++ ++++++++ ++++++++++++++++++++++++++++++++++++ +++++++ +++++++++++++++++++++++++++++++++++ +++++++ ++++++++++++++++++++++++++++++++++++ ++ +++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Plus c’est court, plus c’est bon !

Page 320: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Pourquoi les lambdas ont-ils été introduits dans Java 8 ?

Réponse : parce que c’est à la mode !

Parce que le code écrit est plus compact !

Page 321: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Pourquoi les lambdas ont-ils été introduits dans Java 8 ?

Parce que les lambdas autorisent des nouveaux patterns

qui permettent de paralléliser les traitements simplement et

de façon sûre

- dont les applications ont besoin

- dont concepteurs d’API ont besoin

Page 322: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Java 8 arrive, il s’agit de la mise à jour la plus importante

depuis 15 ans que Java existe

Page 323: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Java 8 arrive, il s’agit de la mise à jour la plus importante

depuis 15 ans que Java existe

Migrer vers Java 8 va nécessiter du travail pour nous les

développeurs

- auto-formation

- changement de nos habitudes de travail

Page 324: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Java 8 arrive, il s’agit de la mise à jour la plus importante

depuis 15 ans que Java existe

Migrer vers Java 8 va nécessiter du travail pour nous les

développeurs

- auto-formation

- changement de nos habitudes de travail

- convaincre nos boss…

Page 325: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Java 8 arrive, il s’agit de la mise à jour la plus importante

depuis 15 ans que Java existe

Téléchargeons la version développeur sur openjdk.net

Date de sortie : 18 mars 2014

Page 326: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Java 8 arrive, il s’agit de la mise à jour la plus importante

depuis 15 ans que Java existe

« Java is a blue collar language. It’s not PhD thesis

material but a language for a job » – James Gosling, 1997

Page 327: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Java 8 arrive, il s’agit de la mise à jour la plus importante

depuis 15 ans que Java existe

« Language features are not a goal unto themselves;

language features are enablers, encouraging or

discouraging certain styles and idioms » – Brian Goetz,

2013

Page 328: JDK 8, lambdas, streams, collectors - Bretagne Tour

Conclusion

Java 8 arrive, il s’agit de la mise à jour la plus importante

depuis 15 ans que Java existe

La bonne nouvelle :

Java est toujours Java !

Page 329: JDK 8, lambdas, streams, collectors - Bretagne Tour
Page 330: JDK 8, lambdas, streams, collectors - Bretagne Tour