JDK 8 & Lambdas 
Lambdas, Streams, Collectors
Pourquoi ont-ils été introduits ?
Pourquoi ont-ils été introduits ? Comment vont-ils changer nos habitudes de programmation ?
Année d’édition 
1994
Année d’édition 
1994 
En stock. Expédié et vendu par Amazon. Emballage cadeau disponible.
Formation Java 8 
6 & 7 février 2014 
BBL
Introduisons les lambdas
Introduisons les lambdas sur un exemple simple
Un exemple très simple 
public class Person { 
private String name ; 
private int age ; 
// constructors 
// getters / set...
int sum = 0 ; 
// I need a default value in case 
// the list is empty 
int average = 0 ; 
for (Person person : list) { 
s...
int sum = 0 ; 
int n = 0 ; 
int average = 0 ; 
for (Person person : list) { 
if (person.getAge() > 20) { 
n++ ; 
sum += pe...
« programmation impérative » 
int sum = 0 ; 
int n = 0 ; 
int average = 0 ; 
for (Person person : list) { 
if (person.getA...
select avg(age) 
from Person 
where age > 20 
… pas une obligation ! 
Ici on décrit le résultat
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 197...
Person 
age 
age > 20 
sum 
1ère étape : mapping 
map
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...
2ème étape : filtrage 
filter 
map 
Person 
age 
age > 20 
sum
Filtrage : 
-prend une liste d’un type donné 
-retourne une liste du même type 
-mais avec moins d’éléments 
Person 
age 
...
3ème étape : réduction 
reduce 
filter 
map 
Person 
age 
age > 20 
sum
Reduction : agrégation des éléments d’une liste dans un seul élément Ex : moyenne, somme, min, max, etc… 
Person 
age 
age...
Comment peut-on modéliser ce traitement ?
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) ; 
}
Mapper<Person, Integer> mapper = new Mapper<Person, Integer>() { 
public Integer map(Person p) { 
return p.getAge() ; 
} 
...
AgePredicate predicate = new Predicate<Integer>() { 
public boolean filter(Integer i) { 
return i > 20 ; 
} 
} 
public int...
Reducer<Integer> reduction = new Reducer<Integer>() { 
public Integer reduce(Integer i1, Integer i2) { 
return i1 + i2 ; 
...
Au final, le pattern map / filter / reduce en JDK 7 : 
1)Créer 3 interfaces 
public interface Mapper<T, V> { public V map(...
List<Person> persons = ... ; 
int sum = 
persons.map( 
new Mapper<Person, Integer>() { 
public Integer map(Person p) { 
re...
List<Person> persons = ... ; int sum = persons.map( new Mapper<Person, Integer>() { public Integer map(Person p) { return ...
À la façon du JDK 8
mapper = new Mapper<Person, Integer>() { 
public Integer map(Person person) { 
return person.getAge() ; 
} 
} 
La façon du...
mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } } 
Pr...
mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return p.getAge() ; } } 
mapper ...
mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } } 
ma...
mapper = new Mapper<Person, Integer>() { 
public Integer map(Person person) { // 1 méthode 
return person.getAge() ; 
} 
}...
Prenons l’exemple du Mapper 
Le compilateur reconnaît cette expression comme une implémentation du mapper 
mapper = new Ma...
Que se passe-t-il si … 
… il y a plus d’une ligne de code ? 
Accolades, un return explicite 
mapper = (Person person) -> {...
Que se passe-t-il si … 
…le type de retour est void ? 
consumer = (Person person) -> p.setAge(p.getAge() + 1) ;
Que se passe-t-il si … 
…la méthode prend plus d’un argument ? 
Ou : 
reducer = (int i1, int i2) -> { 
return i1 + i2 ; 
}...
La façon du JDK 8 
Comment le compilateur reconnaît-il l’implémentation du mapper ? 
mapper = (Person person) -> person.ge...
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 ...
D’autres lambdas 
On peut écrire d’autres lambdas facilement : 
mapper = (Person person) -> person.getAge() ; // mapper fi...
Et la plupart du temps, le compilateur reconnaît ceci : 
Le type des paramètres peut être omis 
mapper = person -> person....
Une remarque sur la réduction 
Comment cela fonctionne-t-il réellement ?
Réduction 
2 exemples : 
Attention : 
le résultat est toujours reproductible en série 
il ne l’est en général pas en paral...
Pour le moment 
Une expression lambda est une autre façon 
d’écrire des instances de classes anonymes
Il y a d’autres syntaxes 
On peut écrire : 
Mais on peut aussi écrire : 
mapper = person -> person.getAge() ; 
mapper = Pe...
Il y a d’autres syntaxes 
On peut écrire : 
Ou encore : 
sum = (i1, i2) -> i1 + i2 ; 
sum = Integer::sum ; // méthode stat...
Il y a d’autres syntaxes 
Encore un autre exemple : 
toLower = String::toLowerCase ; 
// !!!! NON NON NON !!!! 
toLowerFR ...
Plus loin sur les lambdas
Questions : 
Comment modéliser une expression lambda ?
Questions : 
Comment modéliser une expression lambda ? 
Puis-je mettre une expression lambda dans une variable ?
Questions : 
Comment modéliser une expression lambda ? 
Puis-je mettre une expression lambda dans une variable ? 
Un lambd...
Modélisation 
Un lambda = instance d’une « interface fonctionnelle » 
@FunctionalInterface 
public interface Consumer<T> {...
Modélisation 
Un lambda = instance d’une « interface fonctionnelle » 
- ne possède qu’une unique méthode 
@FunctionalInter...
Modélisation 
Un lambda = instance d’une « interface fonctionnelle » 
- ne possède qu’une unique méthode 
- peut être anno...
Mettre un lambda dans une variable 
Exemple d’un consommateur : 
Consumer<String> c = new Consumer<String>() { 
@Override ...
Questions : 
Quel modèle pour un lambda ? 
réponse : une interface fonctionnelle 
Puis-je mettre un lambda dans une variab...
Un lambda est-il un objet ? 
Petit jeu des 7 erreurs (avec une seule erreur) 
Consumer<String> c = new Consumer<String>() ...
Un lambda est-il un objet ? 
Petit jeu des 7 erreurs (avec une seule erreur) 
Consumer<String> c = new Consumer<String>() ...
Questions : 
Quel modèle pour un lambda ? 
réponse : une interface fonctionnelle 
Puis-je mettre un lambda dans une variab...
Des lambdas à profusion : 
Java.util.functions
Java.util.functions 
C’est dans ce package que sont les interfaces fonctionnelles 
Il y en a 43
Java.util.functions 
Supplier 
Consumer / BiConsumer 
Function / BiFunction (UnaryOperator / BinaryOperator) 
Predicate / ...
Supplier 
Un supplier fournit un objet 
public interface Supplier<T> { 
T get() ; 
}
Consumer 
Un consommateur consomme un objet 
public interface Consumer<T> { 
void accept(T t) ; 
} 
Consumer<String> c1 = ...
Function 
Une fonction prend un objet et retourne un autre objet 
Les fonctions peuvent être chaînées et / ou composées 
B...
Predicate 
Un Predicate prend un objet et retourne un booléen 
Il peut être inversé, et composé avec des AND ou OR 
public...
Retour sur 
map / filter / reduce
La façon JDK 7 
Comment implémenter le pattern map / filter / reduce sur List<Person> ?
Mapping d’une liste 
Mapping en JDK 8 avec des lambdas 
List<Person> persons = new ArrayList<>() ; 
List<Integer> ages = 
...
Mapping d’une liste 
Mapping en JDK 8 avec des lambdas 
List<Person> persons = new ArrayList<>() ; 
List<Integer> ages = 
...
Pattern complet 
Le pattern va ressembler à ça : 
Pour : l’API peut être optimisée sans que l’on ait à toucher au code, su...
Pattern complet 
Le pattern va ressembler à ça : 
Pour : l’API peut être optimisée sans que l’on ait à toucher au code, su...
Pattern complet 
1) Supposons que persons soit vraiment GRANDE 
// pattern map / filter / reduce 
List<Person> persons = ....
Pattern complet 
1) Supposons que persons soit vraiment GRANDE 
2 duplications : ages & agesGT20 
// pattern map / filter ...
Pattern complet 
1) Supposons que persons soit vraiment GRANDE 
2 duplications : ages & agesGT20 
Que fait-on de ces liste...
Pattern complet 
2) Supposons que nous aillons un reducer : allMatch 
allMatch est vrai si tous les noms sont plus courts ...
Écriture de allMatch() 
Voici une implémentation basique de allMatch() 
public static <T> boolean allMatch( 
List<? extend...
Pattern complet 
Quand on applique la réduction allMatch()… 
… names a déjà été évaluée ! 
// pattern map / filter / reduc...
Pattern complet 
Quand on applique la réduction allMatch()… 
… names a déjà été évaluée ! 
Il aurait fallu appliquer le ma...
Conclusion 
Pour : 1 
Contre : 2 (au moins) 
// pattern map / filter / reduce 
List<Person> persons = ... ; 
List<Integer>...
// pattern map / filter / reduce 
List<Person> persons = ... ; 
List<Integer> ages = Lists.map( persons, p -> p.getAge()) ...
Conclusion 
Et il en va de même pour celui-ci : 
// pattern map / filter / reduce 
List<Person> persons = ... ; 
List<Inte...
Conclusion (again) 
On a besoin d’un nouveau concept pour traiter les listes de grande taille de façon efficace
Quel pattern choisir ?
Introduction 
Implémenter le map / filter / reduce sur Collection aurait mené à ceci : 
Et même si cette approche n’est pa...
Introduction 
Conservons le même genre de pattern, en ajoutant un appel intermédiaire 
// map / filter / reduce pattern su...
Introduction 
Collection.stream() retourne un Stream : une nouvelle interface 
Nouvelle interface = on a les mains libres ...
Nouvelle interface Collection 
Donc on a besoin d’une nouvelle méthode sur Collection 
public interface Collection<E> { 
/...
Nouvelle interface Collection 
Problème : ArrayList ne compile plus… 
Une solution doit être trouvée ! 
public interface C...
Nouvelles interfaces
Interfaces Java 8 
Problème : ajouter des méthodes à une interface sans toucher aux implémentations…
Problème : ajouter des méthodes à une interface sans toucher aux implémentations… 
Solution : changer la façon dont les in...
Interfaces Java 8 
ArrayList a besoin de l’implémentation de stream()… 
public interface Collection<E> { 
// nos bonnes vi...
Interfaces Java 8 
Solution : mettons-les dans l’interface ! 
public interface Collection<E> { 
// nos bonnes vieilles mét...
Interfaces Java 8 
Une interface devient-elle une classe abstraite ?
Interfaces Java 8 
Une interface devient-elle une classe abstraite ? 
Non ! 
-une classe abstraite peut avoir des champs e...
Méthodes par défaut 
Cela amène-t-il l’héritage multiple en Java ?
Méthodes par défaut 
Cela amène-t-il l’héritage multiple en Java ? 
Oui, mais on l’a déjà
Méthodes par défaut 
Cela amène-t-il l’héritage multiple en Java ? 
Oui, mais on l’a déjà 
public class String 
implements...
Méthodes par défaut 
Ce que l’on a en Java est l’héritage multiple de type
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émentat...
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émentat...
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émentat...
Méthodes par défaut 
Peut-on avoir des conflits ?
Méthodes par défaut 
Peut-on avoir des conflits ? 
Hélas oui…
Méthodes par défaut 
Peut-on avoir des conflits ? 
Hélas oui… 
Il nous faut donc des règles pour les gérer
Méthodes par défaut 
public class C 
implements A, B { 
// ... 
}
Méthodes par défaut 
Exemple #1 
public class C 
implements A, B { 
// ... 
} 
public interface A { 
default String a() { ...
Méthodes par défaut 
Exemple #1 
Erreur de compilation : 
class C inherits unrelated defaults for a() from types A and B 
...
Méthodes par défaut 
Exemple #2 
La classe gagne ! 
public class C 
implements A, B { 
public String a() { ... } 
} 
publi...
Méthodes par défaut 
Exemple #3 
Le plus spécifique gagne ! 
public interface A extends B { 
default String a() { ... } 
}...
Méthodes par défaut 
Retour sur l’exemple #1 
On peut aussi faire un appel explicite 
public interface A { 
default String...
2 règles simples 
Pour gérer les conflits de l’héritage multiple d’implémentation
2 règles simples 
Pour gérer les conflits de l’héritage multiple d’implémentation 
1)La classe gagne
2 règles simples 
Pour gérer les conflits de l’héritage multiple d’implémentation 
1)La classe gagne 
2)Les implémentation...
Un exemple 
On a tous écrit une implémentation d’Iterator : 
public class MyIterator implements Iterator<E> { 
// du code ...
Un exemple 
Grâce à Java 8 : 
Plus besoin d’écrire ce code ! 
public interface Iterator<E> { default void remove() { throw...
Interfaces en Java 8 
Donc on change le concept d’interfaces en Java 8 
public class HelloWorld { 
// souvenir souvenir 
p...
Interfaces en Java 8 
Donc on change le concept d’interfaces en Java 8 
Vraiment… 
public interface HelloWorld { 
// souve...
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’app...
On en sommes-nous ? 
1)On a une nouvelle syntaxe : les lambdas 
2)On a un pattern à implémenter 
3)On a des nouvelles inte...
L’API Stream
Qu’est-ce qu’un Stream ? 
D’un point de vue technique : une interface paramétrée 
« un stream de String »
Qu’est-ce qu’un Stream ? 
D’un point de vue technique : une interface paramétrée 
D’autres interfaces pour les types primi...
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érat...
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...
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 êtr...
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 êtr...
Comment construire un Stream ? 
De nombreuses façons de faire… 
1)À partir d’une collection : 
Collection<String> collecti...
Comment construire un Stream ? 
De nombreuses façons de faire… 
1)À partir d’une collection : 
2)À partir d’un tableau : 
...
Comment construire un Stream ? 
De nombreuses façons de faire… 
1)À partir d’une collection : 
2)À partir d’un tableau : 
...
Comment construire un Stream ? 
Encore quelques patterns : 
Stream.empty() ; // Stream vide 
Stream.of(T t) ; // un seul é...
Comment construire un Stream ? 
Encore d’autres façons : 
string.chars() ; // retourne un IntStream 
lineNumberReader.line...
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 i...
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 i...
Un premier exemple 
Retour sur notre map / filter / reduce 
// map / filter / reduce pattern on collections 
int sum = per...
Un premier exemple 
Retour sur notre map / filter / reduce 
// map / filter / reduce pattern on collections int sum = pers...
Un premier exemple 
Retour sur notre map / filter / reduce 
// map / filter / reduce pattern on collections 
int sum = per...
Un premier exemple 
Retour sur notre map / filter / reduce 
// map / filter / reduce pattern on collections int sum = pers...
Un Stream possède un état 
Les implémentations de Stream possèdent un état : 
-SIZED = le cardinal du Stream est connu 
-O...
Un Stream possède un état 
Certaines opérations changent cet état 
-le filtrage annule SIZED 
-le mapping annule DISTINCT ...
Un Stream possède un état 
Et cela permet d’optimiser ! 
-un HashSet ne peut pas avoir de doublons…
Un Stream possède un état 
Et cela permet d’optimiser ! 
-un HashSet ne peut pas avoir de doublons… 
-donc distinct() ne f...
Un Stream possède un état 
Et cela permet d’optimiser ! 
-un HashSet ne peut pas avoir de doublons… 
-donc distinct() ne f...
Un Stream possède un état 
Et cela permet d’optimiser ! 
-un HashSet ne peut pas avoir de doublons… 
-donc distinct() ne f...
Opérations stateless / statefull 
Opérations Stateless / statefull 
Certaines opérations sont stateless : 
Cela signifie q...
Opérations stateless / statefull 
Opérations Stateless / statefull 
Certaines opérations sont stateless : 
Pas le cas de t...
Exemple 
Trions un tableau de String 
Random rand = new Random() ; 
String [] strings = new String[10_000_000] ; 
for (int...
Exemple 
Trions un tableau de String 
Soooo Java 7… 
Random rand = new Random() ; 
String [] strings = new String[10_000_0...
Exemple 
Trions un tableau de String 
Meilleur ? 
Random rand = new Random() ; Stream<String> stream = Stream.generate( ()...
Exemple 
Trions un tableau de String 
Meilleur ! 
// Random rand = new Random() ; Stream<String> stream = Stream.generate(...
Exemple 
Trions un tableau de String 
// other way Stream<String> stream = ThreadLocalRandom .current() .longs() // return...
Exemple 
Trions un tableau de String 
// other way 
Stream<String> stream = 
ThreadLocalRandom 
.current() 
.longs() 
.map...
Exemple 
Trions un tableau de String 
// other way 
Stream<String> stream = 
ThreadLocalRandom 
.current() 
.longs() 
.map...
Exemple 
Trions un tableau de String 
// other way 
Stream<String> stream = 
ThreadLocalRandom 
.current() 
.longs() 
.map...
Exemple 
Trions un tableau de String 
// other way 
Stream<String> stream = 
ThreadLocalRandom 
.current() 
.longs() 
.map...
Exemple 
Trions un tableau de String 
// other way 
Stream<String> stream = 
ThreadLocalRandom 
.current() 
.longs() 
.map...
Au final 
1)Il y a des opérations intermédiaires, et des opérations terminales 
2)Seule une opération terminale déclenche ...
Au final 
1)Il y a des opérations intermédiaires, et des opérations terminales 
2)Seule une opération terminale déclenche ...
Parallel Streams
Optimisation 
La première optimisation (après l’exécution lazy) est le parallélisme 
Fork / join permet la programmation p...
Optimisation 
La première optimisation (après l’exécution lazy) est le parallélisme 
Fork / join permet la programmation p...
Construction d’un Stream parallèle 
Deux patterns 
1)Appeler parallelStream() au lieu de stream() 
2)Appeler parallel() su...
Paralléliser est-il aussi simple ? 
En fait oui !
Paralléliser est-il aussi simple ? 
En fait oui ! 
… et non…
Paralléliser est-il aussi simple ? 
Exemple 1 : 
« retourne les premiers 10M éléments » 
persons.stream().limit(10_000_000...
Paralléliser est-il aussi simple ? 
Exemple 1 : performances 
Code 1 
List<Long> list = new ArrayList<>(10_000_100) ; 
for...
Paralléliser est-il aussi simple ? 
Exemple 1 : performances 
Code 2 
Stream<Long> stream = Stream.generate(() -> ThreadLo...
Paralléliser est-il aussi simple ? 
Exemple 1 : performances 
Code 3 
Stream<Long> stream = ThreadLocalRandom.current().lo...
Paralléliser est-il aussi simple ? 
Exemple 1 : performances 
Série 
Parallèle 
Code 1 (for) 
270 ms 
Code 2 (limit) 
310 ...
Paralléliser est-il aussi simple ? 
Exemple 1 : performances 
Série 
Parallèle 
Code 1 (for) 
270 ms 
Code 2 (limit) 
310 ...
Parallélisme 
Le parallélisme implique des calculs supplémentaires la plupart du temps 
Des opérations mal configurées von...
Réductions
La réduction simple 
La somme des âges 
// map / filter / reduce 
int sum = persons.stream() 
.map(p -> p.getAge()) 
.filt...
La réduction simple 
Ca serait bien de pouvoir écrire : 
Mais sum() n’est pas définie sur Stream<T> 
Que voudrait dire « a...
La 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étho...
La réduction simple 
2ème version : 
Résultat pour une liste vide : 0 
// map / filter / reduce 
int sum = persons.stream(...
La réduction simple 
2ème version (encore meilleure) : 
Résultat pour une liste vide : 0 
// map / filter / reduce 
int su...
La réduction simple 
Qu’en est-il de min() et de max() ? 
Quelle valeur par défaut pour max() ? 
// map / filter / reduce ...
Problème des valeurs par défaut 
La notion de « valeur par défaut » est plus complexe qu’il y paraît…
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...
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...
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(...
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(...
Problème des valeurs par défaut 
Donc quelle est la valeur par défaut de max() et min() ?
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 valeu...
Problème des valeurs par défaut 
Donc quel type choisir pour la méthode max() ? 
// map / filter / reduce pattern on colle...
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...
Optionals 
Sans valeur par défaut, il faut trouver autre chose… 
// map / filter / reduce pattern on collections 
Optional...
Optionals 
Que peut-on faire avec OptionalInt ? 
1er pattern : tester s’il contient une valeur 
OptionalInt optionalMax = ...
Optionals 
Que peut-on faire avec OptionalInt ? 
2ème pattern : lire la valeur ou jeter une exception 
OptionalInt optiona...
Optionals 
Que peut-on faire avec OptionalInt ? 
3ème pattern : lire la valeur / retourner une valeur par défaut 
Optional...
Optionals 
Que peut-on faire avec OptionalInt ? 
4ème pattern : lire la valeur ou jeter une exception 
OptionalInt optiona...
Available Optionals 
Optional<T> 
OptionalInt, OptionalLong, OptionalDouble
Réductions disponibles 
Sur Stream<T> : 
-reduce() 
-count(), min(), max() 
-anyMatch(), allMatch(), noneMatch() 
-findFir...
Réductions disponibles 
Sur IntStream, LongStream, DoubleStream : 
-average() 
-sum() 
-summaryStatistics()
Mutable reductions
Mutable reductions : exemple 1 
Utilisation d’une classe helper : Collectors 
ArrayList<String> strings = 
stream 
.map(Ob...
Mutable reductions : exemple 2 
Concaténation de String avec un helper 
String names = persons 
.stream() 
.map(Person::ge...
Mutable reductions 
Une réduction mutable dépend : 
-d’un container : Collection or StringBuilder 
-d’un moyen d’ajouter u...
Collectors
La classe Collectors 
Une boite à outils (37 méthodes) pour la plupart des types de réduction 
-counting, minBy, maxBy 
-s...
La classe Collectors 
Average, Sum, Count 
persons 
.stream() 
.collect(Collectors.averagingDouble(Person::getAge)) ; 
per...
La classe Collectors 
Concaténation des noms dans une String 
String names = persons 
.stream() 
.map(Person::getName) 
.c...
La classe Collectors 
Accumulation dans un Set 
Set<Person> setOfPersons = persons 
.stream() 
.collect( 
Collectors.toSet...
La classe Collectors 
Accumulation dans une collection passée en paramètre 
TreeSet<Person> treeSetOfPersons = persons .st...
La classe Collectors 
Calculer un max avec un comparateur 
Bonus : une API Comparator 
Optional<Person> optionalPerson = p...
Construction de comparateurs 
Nouvelle API pour construire des comparateurs 
Comparator<Person> comp = 
Comparator.compari...
Classe Collectors : mapping 
La méthode mapping() prend 2 paramètres 
-une fonction, qui mappe les éléments du Stream 
-un...
Classe Collectors : mapping 
Accumuler les noms des personnes dans un Set 
Set<String> set = persons 
.stream() 
.collect(...
Classe Collectors : mapping 
Mapper le stream, accumulation dans une collection 
TreeSet<String> set = persons 
.stream() ...
Classe Collectors : groupingBy 
« Grouping by » construit des tables de hachage 
-méthode de construction des clés 
-par d...
Classe Collectors : groupingBy 
Des personnes par âge 
Map<Integer, List<Person>> map = 
persons 
.stream() 
.collect( 
Co...
Classe Collectors : groupingBy 
… rangées dans des Set 
Map<Integer, Set<Person>> map = 
persons 
.stream() 
.collect( 
Co...
Classe Collectors : groupingBy 
… on ne garde que les noms 
Map<Integer, Set<String>> map = 
persons 
.stream() 
.collect(...
Classe Collectors : groupingBy 
… les noms rangés dans des TreeSet 
Map<Integer, TreeSet<String>> map = 
persons 
.stream(...
Classe Collectors : groupingBy 
… etc… etc… etc… 
TreeMap<Integer, TreeSet<String>> map = 
persons 
.stream() 
.collect( 
...
Classe Collectors : groupingBy 
Exemple : création d’un histogramme des âges 
Donne le nombre de personnes par âge 
Map<In...
Classe Collectors : partionningBy 
Crée une Map<Boolean, …> à partir d’un prédicat 
-la table a deux clés : TRUE et FALSE ...
Classe Collectors : partionningBy 
Crée une Map<Boolean, …> avec un prédicat 
map.get(TRUE) retourne la liste des personne...
Classe Collectors : partionningBy 
On peut définir d’autres traitements 
Map<Boolean, TreeSet<String>> map = 
persons 
.st...
Classe Collectors : collectingAndThen 
Collecte les données avec un downstream 
Applique enfin une fonction appelée « fini...
Classe Collectors : collectingAndThen 
Dans ce cas « Map::entrySet » est un finisher 
Set<Map.Entry<Integer, List<Person>>...
Quelques exemples réels
1er exemple 
Optional<Entry<Integer, Long>> opt = 
movies.stream().parallel() 
.collect( 
Collectors.collectingAndThen( 
C...
1er exemple 
Optional<Entry<Integer, Long>> opt = 
movies.stream().parallel() 
.collect( 
Collectors.collectingAndThen( 
C...
1er exemple 
Optional<Entry<Integer, Long>> opt = 
movies.stream().parallel() 
.collect( 
Collectors.collectingAndThen( 
C...
1er exemple 
Optional<Entry<Integer, Long>> opt = 
movies.stream().parallel() 
.collect( 
Collectors.collectingAndThen( 
C...
1er exemple 
Optional<Entry<Integer, Long>> opt = 
movies.stream().parallel() 
.collect( 
Collectors.collectingAndThen( 
C...
1er exemple 
Optional<Entry<Integer, Long>> opt = 
movies.stream().parallel() 
.collect( 
Collectors.collectingAndThen( 
C...
Conclusion
Conclusion 
Pourquoi les lambdas ont-ils été introduits dans Java 8 ?
Conclusion 
Pourquoi les lambdas ont-ils été introduits dans Java 8 ? 
Réponse : parce que c’est à la mode !
Conclusion 
Pourquoi les lambdas ont-ils été introduits dans Java 8 ? 
Réponse : parce que c’est à la mode !
Conclusion 
Pourquoi les lambdas ont-ils été introduits dans Java 8 ? 
Réponse : parce que c’est à la mode ! 
Parce que le...
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(flo...
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, ...
Conclusion 
Pourquoi les lambdas ont-ils été introduits dans Java 8 ? 
Réponse : parce que c’est à la mode ! 
Parce que le...
Conclusion 
Pourquoi les lambdas ont-ils été introduits dans Java 8 ? 
Parce que les lambdas autorisent des nouveaux patte...
Conclusion 
Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe
Conclusion 
Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe 
Migrer vers Java ...
Conclusion 
Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe 
Migrer vers Java ...
Conclusion 
Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe 
Téléchargeons la ...
Conclusion 
Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe 
« Java is a blue ...
Conclusion 
Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe 
« Language featur...
Conclusion 
Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe 
La bonne nouvelle...
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
Prochain SlideShare
Chargement dans…5
×

20140123 java8 lambdas_jose-paumard-soat

584 vues

Publié le

0 commentaire
2 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
584
Sur SlideShare
0
Issues des intégrations
0
Intégrations
2
Actions
Partages
0
Téléchargements
38
Commentaires
0
J’aime
2
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

20140123 java8 lambdas_jose-paumard-soat

  1. 1. JDK 8 & Lambdas Lambdas, Streams, Collectors
  2. 2. Pourquoi ont-ils été introduits ?
  3. 3. Pourquoi ont-ils été introduits ? Comment vont-ils changer nos habitudes de programmation ?
  4. 4. Année d’édition 1994
  5. 5. Année d’édition 1994 En stock. Expédié et vendu par Amazon. Emballage cadeau disponible.
  6. 6. Formation Java 8 6 & 7 février 2014 BBL
  7. 7. Introduisons les lambdas
  8. 8. Introduisons les lambdas sur un exemple simple
  9. 9. 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
  10. 10. 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
  11. 11. 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
  12. 12. « 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
  13. 13. select avg(age) from Person where age > 20 … pas une obligation ! Ici on décrit le résultat
  14. 14. 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
  15. 15. Person age age > 20 sum 1ère étape : mapping map
  16. 16. 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
  17. 17. 2ème étape : filtrage filter map Person age age > 20 sum
  18. 18. 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
  19. 19. 3ème étape : réduction reduce filter map Person age age > 20 sum
  20. 20. 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
  21. 21. Comment peut-on modéliser ce traitement ?
  22. 22. 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) ; }
  23. 23. 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) ; }
  24. 24. 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
  25. 25. 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
  26. 26. 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
  27. 27. 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…
  28. 28. 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…
  29. 29. À la façon du JDK 8
  30. 30. mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { return person.getAge() ; } } La façon du JDK 8 Prenons l’exemple du Mapper
  31. 31. 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
  32. 32. 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
  33. 33. 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
  34. 34. 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
  35. 35. 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
  36. 36. 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() ; }
  37. 37. Que se passe-t-il si … …le type de retour est void ? consumer = (Person person) -> p.setAge(p.getAge() + 1) ;
  38. 38. 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 ;
  39. 39. La façon du JDK 8 Comment le compilateur reconnaît-il l’implémentation du mapper ? mapper = (Person person) -> person.getAge() ;
  40. 40. 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
  41. 41. 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
  42. 42. 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
  43. 43. Une remarque sur la réduction Comment cela fonctionne-t-il réellement ?
  44. 44. 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
  45. 45. Pour le moment Une expression lambda est une autre façon d’écrire des instances de classes anonymes
  46. 46. 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
  47. 47. 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 !
  48. 48. Il y a d’autres syntaxes Encore un autre exemple : toLower = String::toLowerCase ; // !!!! NON NON NON !!!! toLowerFR = String::toLowerCase(Locale.FRANCE) ;
  49. 49. Plus loin sur les lambdas
  50. 50. Questions : Comment modéliser une expression lambda ?
  51. 51. Questions : Comment modéliser une expression lambda ? Puis-je mettre une expression lambda dans une variable ?
  52. 52. Questions : Comment modéliser une expression lambda ? Puis-je mettre une expression lambda dans une variable ? Un lambda est-il un objet ?
  53. 53. Modélisation Un lambda = instance d’une « interface fonctionnelle » @FunctionalInterface public interface Consumer<T> { public void accept(T t) ; }
  54. 54. 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) ; }
  55. 55. 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) ; }
  56. 56. 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 :
  57. 57. 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 ?
  58. 58. 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) ;
  59. 59. 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) ;
  60. 60. 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
  61. 61. Des lambdas à profusion : Java.util.functions
  62. 62. Java.util.functions C’est dans ce package que sont les interfaces fonctionnelles Il y en a 43
  63. 63. Java.util.functions Supplier Consumer / BiConsumer Function / BiFunction (UnaryOperator / BinaryOperator) Predicate / BiPredicate En plus des versions construites sur les types primitifs
  64. 64. Supplier Un supplier fournit un objet public interface Supplier<T> { T get() ; }
  65. 65. 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) ;
  66. 66. 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) ; }
  67. 67. 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) ; }
  68. 68. Retour sur map / filter / reduce
  69. 69. La façon JDK 7 Comment implémenter le pattern map / filter / reduce sur List<Person> ?
  70. 70. 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() ) ;
  71. 71. Mapping d’une liste Mapping en JDK 8 avec des lambdas List<Person> persons = new ArrayList<>() ; List<Integer> ages = Lists.map( persons, Person::getAge ) ;
  72. 72. 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) ;
  73. 73. 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) ;
  74. 74. 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) ;
  75. 75. 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) ;
  76. 76. 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) ;
  77. 77. Pattern complet 2) 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) ;
  78. 78. É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 !
  79. 79. 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) ;
  80. 80. 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) ;
  81. 81. Conclusion Pour : 1 Contre : 2 (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) ;
  82. 82. // 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 : 2 (au moins)
  83. 83. 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) ;
  84. 84. Conclusion (again) On a besoin d’un nouveau concept pour traiter les listes de grande taille de façon efficace
  85. 85. Quel pattern choisir ?
  86. 86. 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) ;
  87. 87. 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) ;
  88. 88. 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) ;
  89. 89. 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() ; }
  90. 90. 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() ; }
  91. 91. Nouvelles interfaces
  92. 92. Interfaces Java 8 Problème : ajouter des méthodes à une interface sans toucher aux implémentations…
  93. 93. 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
  94. 94. Interfaces Java 8 ArrayList a besoin de l’implémentation de stream()… public interface Collection<E> { // nos bonnes vieilles méthodes Stream<E> stream() ; }
  95. 95. Interfaces Java 8 Solution : mettons-les dans l’interface ! public interface Collection<E> { // nos bonnes vieilles méthodes default Stream<E> stream() { return ... ; } }
  96. 96. Interfaces Java 8 Une interface devient-elle une classe abstraite ?
  97. 97. Interfaces Java 8 Une interface devient-elle une classe abstraite ? Non ! -une classe abstraite peut avoir des champs et possède un constructeur -un appel de méthode d’une classe abstraite (ou pas) est résolu à l’exécution -une implémentation par défaut est juste du code, lié à la compilation
  98. 98. Méthodes par défaut Cela amène-t-il l’héritage multiple en Java ?
  99. 99. Méthodes par défaut Cela amène-t-il l’héritage multiple en Java ? Oui, mais on l’a déjà
  100. 100. Méthodes par défaut Cela amène-t-il l’héritage multiple en Java ? Oui, mais on l’a déjà public class String implements Serializable, Comparable<String>, CharSequence { // ... }
  101. 101. Méthodes par défaut Ce que l’on a en Java est l’héritage multiple de type
  102. 102. 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
  103. 103. 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
  104. 104. 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 !
  105. 105. Méthodes par défaut Peut-on avoir des conflits ?
  106. 106. Méthodes par défaut Peut-on avoir des conflits ? Hélas oui…
  107. 107. Méthodes par défaut Peut-on avoir des conflits ? Hélas oui… Il nous faut donc des règles pour les gérer
  108. 108. Méthodes par défaut public class C implements A, B { // ... }
  109. 109. 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() { ... } }
  110. 110. 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() { ... } }
  111. 111. Méthodes par défaut Exemple #2 La classe gagne ! public class C implements A, B { public String a() { ... } } public interface A { default String a() { ... } } public interface B { default String a() { ... } }
  112. 112. Méthodes par défaut Exemple #3 Le plus spécifique gagne ! public interface A extends B { default String a() { ... } } public interface B { default String a() { ... } } public class C implements A, B { // ... }
  113. 113. 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() ; } }
  114. 114. 2 règles simples Pour gérer les conflits de l’héritage multiple d’implémentation
  115. 115. 2 règles simples Pour gérer les conflits de l’héritage multiple d’implémentation 1)La classe gagne
  116. 116. 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
  117. 117. 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 !") ; } ; }
  118. 118. Un exemple Grâce à Java 8 : Plus besoin d’écrire ce code ! public interface Iterator<E> { default void remove() { throw new UnsupportedOperationException("remove") ; } ; }
  119. 119. 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!") ; } ; }
  120. 120. 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!") ; } ; }
  121. 121. 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
  122. 122. 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
  123. 123. L’API Stream
  124. 124. Qu’est-ce qu’un Stream ? D’un point de vue technique : une interface paramétrée « un stream de String »
  125. 125. 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
  126. 126. 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
  127. 127. 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
  128. 128. 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
  129. 129. 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
  130. 130. Comment construire un Stream ? De nombreuses façons de faire… 1)À partir d’une collection : Collection<String> collection = ... ; Stream<String> stream = collection.stream() ;
  131. 131. 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"}) ;
  132. 132. 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") ;
  133. 133. 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) ;
  134. 134. 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
  135. 135. 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
  136. 136. 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
  137. 137. 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
  138. 138. 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
  139. 139. 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
  140. 140. 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
  141. 141. 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)
  142. 142. Un Stream possède un état Certaines opérations changent cet état -le filtrage annule SIZED -le mapping annule DISTINCT et SORTED
  143. 143. Un Stream possède un état Et cela permet d’optimiser ! -un HashSet ne peut pas avoir de doublons…
  144. 144. 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
  145. 145. 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…
  146. 146. 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… -sorted() ne fait rien pour un stream construit sur un TreeSet
  147. 147. 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()) ;
  148. 148. 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) ; // sélection des premiers 10M éléments
  149. 149. 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()) ; }
  150. 150. 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()) ; }
  151. 151. Exemple Trions un tableau de String Meilleur ? Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(rand.nextLong()) ) ;
  152. 152. Exemple Trions un tableau de String Meilleur ! // Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(ThreadLocalRandom.current().nextLong()) ) ;
  153. 153. Exemple Trions un tableau de String // other way Stream<String> stream = ThreadLocalRandom .current() .longs() // returns a LongStream .mapToObj(l -> Long.toHexString(l)) ;
  154. 154. Exemple Trions un tableau de String // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) ;
  155. 155. 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
  156. 156. 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() ;
  157. 157. 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
  158. 158. 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 Opérations intermédiaires !
  159. 159. 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
  160. 160. 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
  161. 161. Parallel Streams
  162. 162. 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
  163. 163. 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
  164. 164. 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() ;
  165. 165. Paralléliser est-il aussi simple ? En fait oui !
  166. 166. Paralléliser est-il aussi simple ? En fait oui ! … et non…
  167. 167. Paralléliser est-il aussi simple ? Exemple 1 : « retourne les premiers 10M éléments » persons.stream().limit(10_000_000) ;
  168. 168. 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()) ; }
  169. 169. 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()) ;
  170. 170. 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()) ;
  171. 171. 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
  172. 172. 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
  173. 173. 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
  174. 174. Réductions
  175. 175. La réduction simple La somme des âges // map / filter / reduce int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  176. 176. La 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 int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ;
  177. 177. La 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 int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ;
  178. 178. La réduction simple 2ème version : Résultat pour une liste vide : 0 // map / filter / reduce int sum = persons.stream() .map(Person::getAge) .filter(a -> a > 20) .mapToInt(Integer::intValue) .sum() ;
  179. 179. La réduction simple 2ème version (encore meilleure) : Résultat pour une liste vide : 0 // map / filter / reduce int sum = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) // .mapToInt(Integer::intValue) .sum() ;
  180. 180. La réduction simple Qu’en est-il de min() et de max() ? Quelle valeur par défaut pour max() ? // map / filter / reduce ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ;
  181. 181. Problème des valeurs par défaut La notion de « valeur par défaut » est plus complexe qu’il y paraît…
  182. 182. 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
  183. 183. 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
  184. 184. 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
  185. 185. 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
  186. 186. Problème des valeurs par défaut Donc quelle est la valeur par défaut de max() et min() ?
  187. 187. 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()
  188. 188. 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() ;
  189. 189. 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() ;
  190. 190. 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 »
  191. 191. 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 » }
  192. 192. 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() ;
  193. 193. 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) ;
  194. 194. 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) ;
  195. 195. Available Optionals Optional<T> OptionalInt, OptionalLong, OptionalDouble
  196. 196. Réductions disponibles Sur Stream<T> : -reduce() -count(), min(), max() -anyMatch(), allMatch(), noneMatch() -findFirst(), findAny() -toArray() -forEach(), forEachOrdered()
  197. 197. Réductions disponibles Sur IntStream, LongStream, DoubleStream : -average() -sum() -summaryStatistics()
  198. 198. Mutable reductions
  199. 199. Mutable reductions : exemple 1 Utilisation d’une classe helper : Collectors ArrayList<String> strings = stream .map(Object::toString) .collect(Collectors.toList()) ;
  200. 200. Mutable reductions : exemple 2 Concaténation de String avec un helper String names = persons .stream() .map(Person::getName) .collect(Collectors.joining()) ;
  201. 201. 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)
  202. 202. Collectors
  203. 203. 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, toMap Et - mapping, groupingBy, partionningBy
  204. 204. La classe Collectors Average, Sum, Count persons .stream() .collect(Collectors.averagingDouble(Person::getAge)) ; persons .stream() .collect(Collectors.counting()) ;
  205. 205. La classe Collectors Concaténation des noms dans une String String names = persons .stream() .map(Person::getName) .collect(Collectors.joining(", ")) ;
  206. 206. La classe Collectors Accumulation dans un Set Set<Person> setOfPersons = persons .stream() .collect( Collectors.toSet()) ;
  207. 207. La classe Collectors Accumulation dans une collection passée en paramètre TreeSet<Person> treeSetOfPersons = persons .stream() .collect( Collectors.toCollection(TreeSet::new)) ;
  208. 208. 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)) ;
  209. 209. Construction de comparateurs Nouvelle API pour construire des comparateurs Comparator<Person> comp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person::getAge) ;
  210. 210. 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
  211. 211. Classe Collectors : mapping Accumuler les noms des personnes dans un Set Set<String> set = persons .stream() .collect( Collectors.mapping( Person::getLastName, Collectors.toSet())) ;
  212. 212. Classe Collectors : mapping Mapper le stream, accumulation dans une collection TreeSet<String> set = persons .stream() .collect( Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new)) ) ;
  213. 213. 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)
  214. 214. Classe Collectors : groupingBy Des personnes par âge Map<Integer, List<Person>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge)) ;
  215. 215. Classe Collectors : groupingBy … rangées dans des Set Map<Integer, Set<Person>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.toSet() // le downstream ) ;
  216. 216. 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() // ) ) ;
  217. 217. 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) ) ) ;
  218. 218. 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) ) ) ;
  219. 219. 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() ) ) ;
  220. 220. 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
  221. 221. 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) ) ;
  222. 222. 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)) ) ) ) ;
  223. 223. Classe Collectors : collectingAndThen Collecte les données avec un downstream Applique enfin une fonction appelée « finisher » Indispensable pour retourner des collections immutables
  224. 224. 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 ) ;
  225. 225. Quelques exemples réels
  226. 226. 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()) ;
  227. 227. 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
  228. 228. 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
  229. 229. 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
  230. 230. 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
  231. 231. 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
  232. 232. Conclusion
  233. 233. Conclusion Pourquoi les lambdas ont-ils été introduits dans Java 8 ?
  234. 234. Conclusion Pourquoi les lambdas ont-ils été introduits dans Java 8 ? Réponse : parce que c’est à la mode !
  235. 235. Conclusion Pourquoi les lambdas ont-ils été introduits dans Java 8 ? Réponse : parce que c’est à la mode !
  236. 236. 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 !
  237. 237. 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
  238. 238. 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 !
  239. 239. 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 !
  240. 240. 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
  241. 241. Conclusion Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe
  242. 242. 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 programmer
  243. 243. 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 programmer -convaincre nos boss…
  244. 244. 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
  245. 245. 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
  246. 246. 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
  247. 247. 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 !

×