JDK 8 & Lambdas

Lambdas, Streams, Collectors
Pourquoi ont-ils été introduits ?
Pourquoi ont-ils été introduits ?
Pourquoi sont-ils si importants ?
Pourquoi ont-ils été introduits ?
Pourquoi sont-ils si importants ?
Comment vont-ils changer nos habitudes de
programmatio...
Introduisons les lambdas
Introduisons les lambdas
sur un exemple simple
Un exemple très simple
public class Person {

Un bon vieux bean

private String name ;
private int age ;
// constructors
/...
Calculons la moyenne des âges des personnes
int sum = 0 ;
// I need a default value in case
// the list is empty
int avera...
Plus dur : pour les personnes de plus de 20 ans
int sum = 0 ;
int n = 0 ;
int average = 0 ;
for (Person person : list) {
i...
Plus dur : pour les personnes de plus de 20 ans
int sum = 0 ;
int n = 0 ;
int average = 0 ;
for (Person person : list) {
i...
… pas une obligation !
select avg(age)
from Person
where age > 20

Ici on décrit le résultat
… pas une obligation !
select avg(age)
from Person
where age > 20

© SQL 1974

Ici on décrit le résultat
Dans ce cas la ba...
map
Person

age

1ère étape : mapping

age > 20

sum
map
Person

age

age > 20

1ère étape : mapping
Mapping :
- prend une liste d’un type donné
- retourne une liste d’un autr...
map
Person

filter
age

2ème étape : filtrage

age > 20

sum
map
Person

filter
age

age > 20

2ème étape : filtrage
Filtrage :
- prend une liste d’un type donné
- retourne une liste ...
map
Person

filter
age

3ème étape : réduction

reduce
age > 20

sum
map
Person

filter
age

reduce
age > 20

sum

3ème étape : réduction
Reduction : agrégation des éléments d’une
liste dans ...
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) ;
}
La façon JDK 7
… et on crée une classe anonyme
public interface Mapper<T, V> {
public V map(T t) ;
}
Mapper<Person, Intege...
La façon JDK 7
On peut faire la même chose pour le filtrage
public interface Predicate<T> {
public boolean filter(T t) ;
}
La façon JDK 7
On peut faire la même chose pour le filtrage
public interface Predicate<T> {
public boolean filter(T t) ;
}...
La façon JDK 7
Et enfin pour la réduction
public interface Reducer<T> {
public T reduce(T t1, T t2) ;
}
La façon JDK 7
Et enfin pour la réduction
public interface Reducer<T> {
public T reduce(T t1, T t2) ;
}
Reducer<Integer> r...
La façon JDK 7
Au final, le pattern map / filter / reduce en JDK 7 :
1) Créer 3 interfaces
public interface Mapper<T, V> {...
La façon JDK 7
Au final, le pattern map / filter / reduce en JDK 7 :
1) Créer 3 interfaces
2) Et on applique…
List<Person>...
La façon JDK 7
Au final, le pattern map / filter / reduce en JDK 7 :
1) Créer 3 interfaces
2) Et on applique…
List<Person>...
À la façon du JDK 8
La façon du JDK 8
Prenons l’exemple du Mapper
mapper = new Mapper<Person, Integer>() {
public Integer map(Person person) {...
La façon du JDK 8
Prenons l’exemple du Mapper
mapper = new Mapper<Person, Integer>() {
public Integer map(Person person) {...
La façon du JDK 8
Prenons l’exemple du Mapper
mapper = new Mapper<Person, Integer>() {
public Integer map(Person person) {...
La façon du JDK 8
Prenons l’exemple du Mapper
mapper = new Mapper<Person, Integer>() {
public Integer map(Person person) {...
La façon du JDK 8
Prenons l’exemple du Mapper
mapper = new Mapper<Person, Integer>() {
public Integer map(Person person) {...
La façon du JDK 8
Prenons l’exemple du Mapper
mapper = new Mapper<Person, Integer>() {
public Integer map(Person person) {...
La façon du JDK 8
Prenons l’exemple du Mapper
mapper = new Mapper<Person, Integer>() {
public Integer map(Person person) {...
Que se passe-t-il si …
… il y a plus d’une ligne de code ?
mapper = (Person person) -> {
System.out.println("Mapping " + p...
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 ?
reducer = (int i1, int i2) -> {
return i1 + i2 ;
}

Ou :
red...
La façon du JDK 8
Comment le compilateur reconnaît-il l’implémentation du
mapper ?
mapper = (Person person) -> person.getA...
La façon du JDK 8
Comment le compilateur reconnaît-il l’implémentation du
mapper ?
mapper = (Person person) -> person.getA...
La façon du JDK 8
Comment le compilateur reconnaît-il l’implémentation du
mapper ?
mapper = (Person person) -> person.getA...
La façon du JDK 8
Comment le compilateur reconnaît-il l’implémentation du
mapper ?
mapper = (Person person) -> person.getA...
D’autres lambdas
On peut écrire d’autres lambdas facilement :
mapper = (Person person) -> person.getAge() ; // mapper
filt...
D’autres lambdas
Et la plupart du temps, le compilateur reconnaît ceci :
mapper = person -> person.getAge() ; // mapper
fi...
Une remarque sur la réduction
Comment cela fonctionne-t-il réellement ?
Réduction
2 exemples :
Reducer r1 = (i1, i2) -> i1 + i2 ; // Ok

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

Atten...
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 :
mapper = person -> person.getAge() ;

Mais on peut aussi écrire :
mapper = Perso...
Il y a d’autres syntaxes
On peut écrire :
sum = (i1, i2) -> i1 + i2 ;
sum = Integer::sum ; // méthode statique, nouvelle !...
Il y a d’autres syntaxes
Encore un autre exemple :
toLower = String::toLowerCase ;

// !!!! NON NON NON !!!!
toLowerFR = S...
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 lambda e...
Modélisation
Un lambda = instance d’une « interface fonctionnelle »
@FunctionalInterface
public interface Consumer<T> {
pu...
Modélisation
Un lambda = instance d’une « interface fonctionnelle »
@FunctionalInterface
public interface Consumer<T> {
pu...
Modélisation
Un lambda = instance d’une « interface fonctionnelle »
@FunctionalInterface
public interface Consumer<T> {
pu...
Mettre un lambda dans une variable
Exemple d’un consommateur :
Consumer<String> c = new Consumer<String>() {
@Override
pub...
Mettre un lambda dans une variable
Exemple d’un consommateur :
Consumer<String> c = new Consumer<String>() {
@Override
pub...
Mettre un lambda dans une variable
Exemple d’un consommateur :
Consumer<String> c = new Consumer<String>() {
@Override
pub...
Mettre un lambda dans une variable
Question : peut-on écrire ce code ?
int i = ... ;
Consumer<String> c = new Consumer<Str...
Mettre un lambda dans une variable
Question : peut-on écrire ce code ?
int i = ... ;
Consumer<String> c = new Consumer<Str...
Mettre un lambda dans une variable
Question : peut-on écrire ce code ?
int i = ... ;
Consumer<String> c = new Consumer<Str...
Mettre un lambda dans une variable
En revanche, ce code …
int i = ... ;
Consumer<String> c = new Consumer<String>() {
@Ove...
Mettre un lambda dans une variable
Raison : le compilateur infère que i est effectivement final
final int i = ... ;
Consum...
Note au sujet de this
JDK 7 : this est l’instance anonyme
Consumer<String> c = new Consumer<String>() {
@Override
public v...
Note au sujet de this
JDK 8 : idem, accept() affiche « je suis dans c »
Consumer<String> c = new Consumer<String>() {
@Ove...
Note au sujet de this
Un consommateur peut aussi s’écrire comme ça :
Consumer<String> c = s -> System.out.println(this) ;
...
Questions :
Quel modèle pour un lambda ?
réponse : une interface fonctionnelle
Puis-je mettre un lambda dans une variable ...
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 variable ...
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 / BiPr...
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 = s ->...
BiConsumer
Prend deux arguments au lieu d’un
Peuvent être chaînés
Versions types primitifs :
- ObjIntConsumer
- ObjLongCon...
Function
Une fonction prend un objet et retourne un autre objet
public interface Function<T, R> {

R apply(T t) ;
}

Les f...
Predicate
Un Predicate prend un objet et retourne un booléen
public interface Predicate<T> {

boolean test(T t) ;
}

Il pe...
BiPredicate
Un BiPredicate prend deux objets et retourne un booléen
public interface BiPredicate<T, U> {

boolean test(T t...
Retour sur
map / filter / reduce
La façon JDK 7
Comment implémenter le pattern map / filter / reduce
sur List<Person> ?
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...
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...
Mapping d’une liste
Mapping en JDK 8 avec des lambdas
List<Person> persons = new ArrayList<>() ;
List<Integer> ages =
List...
Mapping d’une liste
Mapping en JDK 8 avec des lambdas
List<Person> persons = new ArrayList<>() ;
List<Integer> ages =
List...
Pattern complet
Le pattern va ressembler à ça :
// pattern map / filter / reduce
List<Person> persons = ... ;
List<Integer...
Pattern complet
Le pattern va ressembler à ça :
// pattern map / filter / reduce
List<Person> persons = ... ;
List<Integer...
Pattern complet
Le pattern va ressembler à ça :
// pattern map / filter / reduce
List<Person> persons = ... ;
List<Integer...
Pattern complet
Le pattern va ressembler à ça :
// pattern map / filter / reduce
List<Person> persons = ... ;
List<Integer...
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
// pattern map / filter / reduce
List<Person> persons = ... ...
Pattern complet
1) Supposons que persons soit vraiment GRANDE
// pattern map / filter / reduce
List<Person> persons = ... ...
Pattern complet
2) Supposons que les algorithmes de calcul du map / filter /
reduce soient déjà optimisés
// pattern map /...
Pattern complet
2) Supposons que les algorithmes de calcul du map / filter /
reduce soient déjà optimisés
// pattern map /...
Pattern complet
2) Supposons que les algorithmes de calcul du map / filter /
reduce soient déjà optimisés
// pattern map /...
Pattern complet
2) Supposons que l’on veuille paralléliser…
// pattern map / filter / reduce
List<Person> persons = ... ;
...
Pattern complet
3) Supposons que nous aillons un reducer : allMatch
// pattern map / filter / reduce
List<Person> persons ...
Écriture de allMatch()
Voici une implémentation basique de allMatch()
public static <T> boolean allMatch(
List<? extends T...
Écriture de allMatch()
Voici une implémentation basique de allMatch()
public static <T> boolean allMatch(
List<? extends T...
Écriture de allMatch()
Voici une implémentation basique de allMatch()
public static <T> boolean allMatch(
List<? extends T...
Pattern complet
Quand on applique la réduction allMatch()…
// pattern map / filter / reduce
List<Person> persons = ... ;
L...
Pattern complet
Quand on applique la réduction allMatch()…
// pattern map / filter / reduce
List<Person> persons = ... ;
L...
Pattern complet
Quand on applique la réduction allMatch()…
// pattern map / filter / reduce
List<Person> persons = ... ;
L...
Conclusion
Pour : 1
Contre : 3 (au moins)
// pattern map / filter / reduce
List<Person> persons = ... ;
List<Integer> ages...
Conclusion
Pour : 1
Contre : 3 (au moins)
// pattern map / filter / reduce
List<Person> persons = ... ;
List<Integer> ages...
Conclusion
Et il en va de même pour celui-ci :

// pattern map / filter / reduce
List<Person> persons = ... ;
List<Integer...
Conclusion (again)
On a besoin d’un nouveau concept pour traiter les listes de
grande taille de façon efficace
Conclusion (again)
On a besoin d’un nouveau concept pour traiter les listes de
grande taille de façon efficace
Le framewor...
Conclusion (again)
On a besoin d’un nouveau concept pour traiter les listes de
grande taille de façon efficace
Le framewor...
Quel pattern choisir ?
Introduction
Implémenter le map / filter / reduce sur Collection aurait
mené à ceci :
// map / filter / reduce pattern sur...
Introduction
Conservons le même genre de pattern, en ajoutant un
appel intermédiaire
// map / filter / reduce pattern sur ...
Introduction
Collection.stream() retourne un Stream : une nouvelle
interface
// map / filter / reduce pattern sur Collecti...
Nouvelle interface Collection
Donc on a besoin d’une nouvelle méthode sur Collection
public interface Collection<E> {
// n...
Nouvelle interface Collection
Problème : ArrayList ne compile plus…
Une solution doit être trouvée !
public interface Coll...
Interfaces
Problème : ArrayList ne compile plus…
Une solution doit être trouvée !
Une solution qui ne nécessite pas de mod...
Nouvelles interfaces
Interfaces Java 8
Problème : ArrayList ne compile plus…
Une solution doit être trouvée !
Une solution qui ne nécessite pas...
Interfaces Java 8
Problème : ajouter des méthodes à une interface sans
toucher aux implémentations…
Solution : changer la ...
Interfaces Java 8
ArrayList a besoin de l’implémentation de stream()…
public interface Collection<E> {
// nos bonnes vieil...
Interfaces Java 8
Solution : mettons-les dans l’interface !
public interface Collection<E> {
// nos bonnes vieilles méthod...
Interfaces Java 8
Introduisons les default methods en Java
public interface Collection<E> {
// nos bonnes vieilles méthode...
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 mutliple en Java ?
Oui, mais on l’a déjà
public class String
implements Ser...
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émentatio...
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émentatio...
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émentatio...
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
public class C
implements A, B {
// ...
}

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

public i...
Méthodes par défaut
public class C
implements A, B {
public String a() { ... }
}

Exemple #0
public interface A {
default ...
Méthodes par défaut
public class C
implements A, B {
// ...
}

Exemple #1
public interface A {
default String a() { ... }
...
Méthodes par défaut
public class C
implements A, B {
// ...
}

Exemple #1
public interface A {
default String a() { ... }
...
Méthodes par défaut
public class C
implements A, B {
// ...
}

Exemple #2
public interface A extends B {
default String a(...
Méthodes par défaut
public class D extends C
implements B {
// ...
}

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

Exemple #2
p...
Méthodes par défaut
public class D extends C
implements B {
// ...
}

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

Exemple #2
p...
Méthodes par défaut
public class C
implements A, B {
public String a() { return B.super.a() ; }
}

Retour sur l’exemple #1...
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émentations...
Un exemple
On a tous écrit une implémentation d’Iterator :
public class MyIterator implements Iterator<E> {
// du code mét...
Un exemple
Grâce à Java 8 :
public interface Iterator<E> {
default void remove() {
throw new UnsupportedOperationException...
Interfaces en Java 8
Donc on change le concept d’interfaces en Java 8
public class HelloWorld {
// souvenir souvenir
publi...
Interfaces en Java 8
Donc on change le concept d’interfaces en Java 8
Vraiment…
public interface HelloWorld {
// souvenir ...
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’appl...
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

« un stream de String »
D’autres interfac...
Une nouvelle notion
Cela ressemble à une collection, mais…
- Un Stream est construit sur une « source »
- Pas de donnée à ...
Une nouvelle notion
Un Stream peut être construit sur :
- Une collection ou un tableau
- Un itérateur
- Un source I/O
Cert...
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)
2)
3)
4)

Un stream ne porte pas de donnée
Un stream ne peut pas modifier sa source
Une source peut...
Un Stream est-il une Collection ?
La réponse est non
Un Stream est-il une Collection ?
La réponse est non
Une collection permet d’itérer sur ses éléments
Un Stream est-il une Collection ?
La réponse est non
Une collection permet d’itérer sur ses éléments
Pas un Stream
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 est-il une Collection ?
La réponse est non
Une collection ne permet pas d’appliquer un lambda sur
ses éléments
U...
Comment construire un Stream ?
De nombreuses façons de faire…
1) À partir d’une collection :

Collection<String> collectio...
Comment construire un Stream ?
De nombreuses façons de faire…
1) À partir d’une collection :
2) À partir d’un tableau :

S...
Comment construire un Stream ?
De nombreuses façons de faire…
1) À partir d’une collection :
2) À partir d’un tableau :
3)...
Comment construire un Stream ?
Encore quelques patterns :
Stream.empty() ; // Stream vide
Stream.of(T t) ; // un seul élém...
Comment construire un Stream ?
Encore d’autres façons :
string.chars() ; // retourne un IntStream
lineNumberReader.lines()...
Un premier exemple
Retour sur notre map / filter / reduce
Construction d’un
collections
Stream sur une List

// map / filt...
Un premier exemple
Retour sur notre map / filter / reduce
Déclarations
// map / filter / reduce pattern on collections
int...
Un premier exemple
Retour sur notre map / filter / reduce
On lance le
collections
calcul

// map / filter / reduce pattern...
Un premier exemple
Retour sur notre map / filter / reduce
// map / filter / reduce pattern on collections
int sum = person...
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 int...
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 int...
Un Stream possède un état
Les implémentations de Stream possèdent un état :
- SIZED = le cardinal du Stream est connu
- OR...
Un Stream possède un état
Certaines opérations changent cet état
- le filtrage annule SIZED
- le mapping annule DISTINCT e...
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 fa...
Un Stream possède un état
Et cela permet d’optimiser !
- un HashSet ne peut pas avoir de doublons…
- donc distinct() ne fa...
Un Stream possède un état
Et cela permet d’optimiser !
- un HashSet ne peut pas avoir de doublons…
- donc distinct() ne fa...
Un

ème
2

exemple

Trions une liste de chaîne de caractères
List<String> strings = new ArrayList<>() ;
// on remplit la l...
Un

ème
2

exemple

Trions une liste de chaîne de caractères
// un jour un petit malin change l’implémentation
SortedSet<S...
Un

ème
2

exemple

Trions une liste de chaîne de caractères
// un jour un petit malin change l’implémentation
SortedSet<S...
Opérations stateless / statefull
Opérations Stateless / statefull
Certaines opérations sont stateless :
persons.stream().m...
Opérations stateless / statefull
Opérations Stateless / statefull
Certaines opérations sont stateless :
persons.stream().m...
Exemple
Trions un tableau de String
Random rand = new Random() ;
String [] strings = new String[10_000_000] ;
for (int i =...
Exemple
Trions un tableau de String
Random rand = new Random() ;
String [] strings = new String[10_000_000] ;
for (int i =...
Exemple
Trions un tableau de String
Random rand = new Random() ;
Stream<String> stream =
Stream.generate(
() ->
Long.toHex...
Exemple
Trions un tableau de String
// Random rand = new Random() ;
Stream<String> stream =
Stream.generate(
() ->
Long.to...
Exemple
Trions un tableau de String
// other way
Stream<String> stream =
ThreadLocalRandom
.current()
.longs() // returns ...
Exemple
Trions un tableau de String
// other way
Stream<String> stream =
ThreadLocalRandom
.current()
.longs()
.mapToObj(L...
Exemple
Trions un tableau de String
// other way
Stream<String> stream =
ThreadLocalRandom
.current()
.longs()
.mapToObj(L...
Exemple
Trions un tableau de String
// other way
Stream<String> stream =
ThreadLocalRandom
.current()
.longs()
.mapToObj(L...
Exemple
Trions un tableau de String
// other way
Stream<String> stream =
ThreadLocalRandom
.current()
.longs()
.mapToObj(L...
Example
Trions un tableau de String
// other way
Stream<String> stream =
ThreadLocalRandom
.current()
.longs()
.mapToObj(L...
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 par...
Optimisation
La première optimisation (après l’exécution lazy) est le
parallélisme
Fork / join permet la programmation par...
Construction d’un Stream parallèle
Deux patterns
1) Appeler parallelStream() au lieu de stream()
Stream<String> s = string...
Peut-on choisir le nombre cœurs ?
La réponse est non
Peut-on choisir le nombre cœurs ?
La réponse est non
Mais… en a-t-on vraiment besoin ?
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 :
persons.stream().limit(10_000_000) ;

« retourne les premiers 10M éléments ...
Paralléliser est-il aussi simple ?
Exemple 1 :
persons.parallelStream().limit(10_000_000) ;

« retourne les premiers 10M é...
Paralléliser est-il aussi simple ?
Exemple 1 :
persons.parallelStream().limit(10_000_000) ;

« retourne les premiers 10M é...
Paralléliser est-il aussi simple ?
Exemple 1 : performances
Code 1
List<Long> list = new ArrayList<>(10_000_100) ;
for (in...
Paralléliser est-il aussi simple ?
Exemple 1 : performances
Code 2
Stream<Long> stream =
Stream.generate(() -> ThreadLocal...
Paralléliser est-il aussi simple ?
Exemple 1 : performances
Code 3
Stream<Long> stream =
ThreadLocalRandom.current().longs...
Paralléliser est-il aussi simple ?
Exemple 1 : performances

Code 1 (for)
Code 2 (limit)
Code 3 (longs)

Série
270 ms
310 ...
Paralléliser est-il aussi simple ?
Exemple 1 : performances

Code 1 (for)
Code 2 (limit)
Code 3 (longs)

Série
270 ms
310 ...
Paralléliser est-il aussi simple ?
Exemple 2 :
Stream s3 = Stream.concat(stream1, stream2) ;

« retourne un Stream composé...
Parallélisme
Le parallélisme implique des calculs supplémentaires la
plupart du temps
Des opérations mal configurées vont ...
Réductions
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
...
Qu’est-ce qu’une réduction ?
2 types de réduction :
2) La réduction mutable

« A mutable reduction operation accumulates
i...
Une réduction simple
La somme des âges
// map / filter / reduce pattern on collections
int sum = persons.stream()
.map(p -...
Une réduction simple
Ca serait bien de pouvoir écrire :
// map / filter / reduce pattern on collections
int sum = persons....
Une réduction simple
Ca serait bien de pouvoir écrire :
// map / filter / reduce pattern on collections
int sum = persons....
Une réduction simple
2ème version :
// map / filter / reduce pattern on collections
int sum = persons.stream()
.map(Person...
Une réduction simple
2ème version (encore meilleure) :
// map / filter / reduce pattern on collections
int sum = persons.s...
Une réduction simple
Qu’en est-il de min() et de max() ?
// map / filter / reduce pattern on collections
....... = persons...
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
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(e,...
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,...
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 valeur ...
Problème des valeurs par défaut
Donc quel type choisir pour la méthode max() ?
// map / filter / reduce pattern on collect...
Problème des valeurs par défaut
Donc quel type choisir pour la méthode max() ?
// map / filter / reduce pattern on collect...
Optionals
Sans valeur par défaut, il faut trouver autre chose…
// map / filter / reduce pattern on collections
OptionalInt...
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 optionalMa...
Optionals
Que peut-on faire avec OptionalInt ?
3éme pattern : lire la valeur / retourner une valeur par défaut
OptionalInt...
Optionals
Que peut-on faire avec OptionalInt ?
4ème pattern : lire la valeur ou jeter une exception
OptionalInt optionalMa...
Available Optionals
Optional<T>
OptionalInt, OptionalLong, OptionalDouble
Réductions disponibles
Sur Stream<T> :
- reduce()
- count(), min(), max()
- anyMatch(), allMatch(), noneMatch()
- findFirs...
Réductions disponibles
Sur IntStream, LongStream, DoubleStream :
- average()
- summaryStatistics()
Mutable reductions : exemple 1
Utilisation d’une classe helper : Collectors
ArrayList<String> strings =
stream
.map(Object...
Mutable reductions : exemple 2
Concaténation de String avec un helper
String names = persons
.stream()
.map(Person::getNam...
Mutable reductions
Une réduction mutable dépend :
- d’un container : Collection or StringBuilder
- d’un moyen d’ajouter un...
Mutable reductions
La forme générale a donc besoin de 3 objets :
- un supplier : construit une instance du container

Supp...
Mutable reductions
La forme générale a donc besoin de 3 objets :
- un supplier : construit une instance du container
- un ...
Mutable reductions
La forme générale a donc besoin de 3 objets :
- un supplier : construit une instance du container
- un ...
Mutable reductions : exemple 1
D’où le code :
ArrayList<String> strings =
stream
.map(Object::toString)
.collect(
() -> ne...
Mutable reductions : exemple 1
D’où le code :
ArrayList<String> strings =
stream
.map(Object::toString)
.collect(
ArrayLis...
Collectors
La classe Collectors
Une boite à outils (37 méthodes) pour la plupart des types
de réduction
- counting, minBy, maxBy
- su...
La classe Collectors
Average, Sum, Count
persons
.stream()
.collect(Collectors.averagingDouble(Person::getAge)) ;
persons
...
La classe Collectors
Concaténation des nom dans une String
String names = persons
.stream()
.map(Person::getName)
.collect...
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
.stre...
La classe Collectors
Calculer un max avec un comparateur
Optional<Person> optionalPerson = persons
.stream()
.collect(
Col...
Construction de comparateurs
Nouvelle API pour construire des comparateurs
Comparator<Person> comp =
Comparator.comparing(...
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(
Col...
Classe Collectors : mapping
Mapper le stream, accumulation dans une collection
TreeSet<String> set = persons
.stream()
.co...
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(
Collecto...
Classe Collectors : groupingBy
… rangées dans des Set
Map<Integer, Set<Person>> map =
persons
.stream()
.collect(
Collecto...
Classe Collectors : groupingBy
… on ne garde que les noms
Map<Integer, Set<String>> map =
persons
.stream()
.collect(
Coll...
Classe Collectors : groupingBy
… les noms rangés dans des TreeSet
Map<Integer, TreeSet<String>> map =
persons
.stream()
.c...
Classe Collectors : groupingBy
… etc… etc… etc…
TreeMap<Integer, TreeSet<String>> map =
persons
.stream()
.collect(
Collec...
Classe Collectors : groupingBy
Exemple : création d’un histogramme des âges
Map<Integer, Long> map =
persons
.stream()
.co...
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<Boolean, List<Person>> map =
persons
.stre...
Classe Collectors : partionningBy
On peut définir d’autres traitements
Map<Boolean, TreeSet<String>> map =
persons
.stream...
Classe Collectors : collectingAndThen
Collecte les données avec un downstream
Applique enfin une fonction appelée « finish...
Classe Collectors : collectingAndThen
Set<Map.Entry<Integer, List<Person>>> set =
persons
.stream()
.collect(
Collectors.c...
Quelques exemples réels
er
1

exemple

Optional<Entry<Integer, Long>> opt =
movies.stream().parallel()
.collect(
Collectors.collectingAndThen(
Col...
er
1

exemple
Un stream de movies

Optional<Entry<Integer, Long>> opt =
movies.stream().parallel()
.collect(
Collectors.co...
er
1

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

Optional<Entry<Integer, Long>> opt =
movies.stream().parallel()
....
er
1

exemple

Optional<Entry<Integer, Long>> opt =
movies.stream().parallel()
.collect(
Collectors.collectingAndThen(
Col...
er
1

exemple
Et déterminer la plus
grande valeur

Optional<Entry<Integer, Long>> opt =
movies.stream().parallel()
.collec...
er
1

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

Optional<Entry<Integer, Long>> opt =
movies.stream(...
Remarques sur parallel()
Traitement parallèle
Deux façons de le faire :
- créer un stream parallèle d’entrée
- Appeler parallel() sur un stream don...
Les choses qui ne marchent pas
Certaines opérations ne donnent pas des résultats
reproductibles en parallèle, ex : findAny...
Les choses qui marchent pas, mais…
Le traitement parallèle doit être le moins contraint possible
pour être efficace
- ORDE...
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 co...
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...
Plus c’est court, plus c’est bon !
Un exemple de code compact

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++...
Conclusion
Pourquoi les lambdas ont-ils été introduits dans Java 8 ?
Réponse : parce que c’est à la mode !
Parce que le co...
Conclusion
Pourquoi les lambdas ont-ils été introduits dans Java 8 ?
Parce que les lambdas autorisent des nouveaux pattern...
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 8 ...
Conclusion
Java 8 arrive, il s’agit de la mise à jour la plus importante
depuis 15 ans que Java existe
Migrer vers Java 8 ...
Conclusion
Java 8 arrive, il s’agit de la mise à jour la plus importante
depuis 15 ans que Java existe
Téléchargeons la ve...
Conclusion
Java 8 arrive, il s’agit de la mise à jour la plus importante
depuis 15 ans que Java existe
« Java is a blue co...
Conclusion
Java 8 arrive, il s’agit de la mise à jour la plus importante
depuis 15 ans que Java existe
« Language features...
Conclusion
Java 8 arrive, il s’agit de la mise à jour la plus importante
depuis 15 ans que Java existe

La bonne nouvelle ...
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
JDK 8, lambdas, streams, collectors - Bretagne Tour
Prochain SlideShare
Chargement dans…5
×

JDK 8, lambdas, streams, collectors - Bretagne Tour

9 189 vues

Publié le

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.

Publié dans : Formation

JDK 8, lambdas, streams, collectors - Bretagne Tour

  1. 1. JDK 8 & Lambdas Lambdas, Streams, Collectors
  2. 2. Pourquoi ont-ils été introduits ?
  3. 3. Pourquoi ont-ils été introduits ? Pourquoi sont-ils si importants ?
  4. 4. Pourquoi ont-ils été introduits ? Pourquoi sont-ils si importants ? Comment vont-ils changer nos habitudes de programmation ?
  5. 5. Introduisons les lambdas
  6. 6. Introduisons les lambdas sur un exemple simple
  7. 7. Un exemple très simple public class Person { Un bon vieux bean private String name ; private int age ; // constructors // getters / setters } List<Person> list = new ArrayList<>() ; … et une bonne vieille liste
  8. 8. Calculons la moyenne des âges des personnes 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() ; }
  9. 9. Plus dur : pour les personnes de plus de 20 ans 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 ; }
  10. 10. Plus dur : pour les personnes de plus de 20 ans 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 ; } « programmation impérative »
  11. 11. … pas une obligation ! select avg(age) from Person where age > 20 Ici on décrit le résultat
  12. 12. … pas une obligation ! select avg(age) from Person where age > 20 © SQL 1974 Ici on décrit le résultat Dans ce cas la base de données mène le calcul comme elle l’entend
  13. 13. map Person age 1ère étape : mapping age > 20 sum
  14. 14. map Person age age > 20 1ère étape : mapping Mapping : - prend une liste d’un type donné - retourne une liste d’un autre type - possède le même nombre d’éléments sum
  15. 15. map Person filter age 2ème étape : filtrage age > 20 sum
  16. 16. map Person filter age age > 20 2ème étape : filtrage Filtrage : - prend une liste d’un type donné - retourne une liste du même type - mais avec moins d’éléments sum
  17. 17. map Person filter age 3ème étape : réduction reduce age > 20 sum
  18. 18. map Person filter age reduce age > 20 sum 3ème étape : réduction Reduction : agrégation des éléments d’une liste dans un seul élément Ex : moyenne, somme, min, max, etc…
  19. 19. Comment peut-on modéliser ce traitement ?
  20. 20. 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) ; }
  21. 21. La façon JDK 7 … et on crée une classe anonyme 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() ; } }
  22. 22. La façon JDK 7 On peut faire la même chose pour le filtrage public interface Predicate<T> { public boolean filter(T t) ; }
  23. 23. La façon JDK 7 On peut faire la même chose pour le filtrage public interface Predicate<T> { public boolean filter(T t) ; } AgePredicate predicate = new Predicate<Integer>() { public boolean filter(Integer i) { return i > 20 ; } }
  24. 24. La façon JDK 7 Et enfin pour la réduction public interface Reducer<T> { public T reduce(T t1, T t2) ; }
  25. 25. La façon JDK 7 Et enfin pour la réduction public interface Reducer<T> { public T reduce(T t1, T t2) ; } Reducer<Integer> reduction = new Reducer<Integer>() { public Integer reduce(Integer i1, Integer i2) { return i1 + i2 ; } }
  26. 26. La façon JDK 7 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) ; }
  27. 27. La façon JDK 7 Au final, le pattern map / filter / reduce en JDK 7 : 1) Créer 3 interfaces 2) Et on applique… 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 ; } } }) ;
  28. 28. La façon JDK 7 Au final, le pattern map / filter / reduce en JDK 7 : 1) Créer 3 interfaces 2) Et on applique… 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 ; } } }) ;
  29. 29. À la façon du JDK 8
  30. 30. La façon du JDK 8 Prenons l’exemple du Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { return person.getAge() ; } }
  31. 31. La façon du JDK 8 Prenons l’exemple du Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } }
  32. 32. La façon du JDK 8 Prenons l’exemple du Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return p.getAge() ; On prend } } person mapper = (Person person) ;
  33. 33. La façon du JDK 8 Prenons l’exemple du Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } } et… mapper = (Person person) -> ;
  34. 34. La façon du JDK 8 Prenons l’exemple du Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } } … on retourne mapper = (Person person) -> person.getAge() ; son âge
  35. 35. La façon du JDK 8 Prenons l’exemple du Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } } mapper = (Person person) -> person.getAge() ;
  36. 36. La façon du JDK 8 Prenons l’exemple du Mapper mapper = new Mapper<Person, Integer>() { public Integer map(Person person) { // 1 méthode return person.getAge() ; } } mapper = (Person person) -> person.getAge() ; Le compilateur reconnaît cette expression comme une implémentation du mapper
  37. 37. Que se passe-t-il si … … il y a plus d’une ligne de code ? mapper = (Person person) -> { System.out.println("Mapping " + person) ; return person.getAge() ; } Accolades, un return explicite
  38. 38. Que se passe-t-il si … …le type de retour est void ? consumer = (Person person) -> p.setAge(p.getAge() + 1) ;
  39. 39. Que se passe-t-il si … …la méthode prend plus d’un argument ? reducer = (int i1, int i2) -> { return i1 + i2 ; } Ou : reducer = (int i1, int i2) -> i1 + i2 ;
  40. 40. La façon du JDK 8 Comment le compilateur reconnaît-il l’implémentation du mapper ? mapper = (Person person) -> person.getAge() ;
  41. 41. La façon du JDK 8 Comment le compilateur reconnaît-il l’implémentation du mapper ? mapper = (Person person) -> person.getAge() ; 1) Il ne faut qu’une méthode dans le mapper
  42. 42. La façon du JDK 8 Comment le compilateur reconnaît-il l’implémentation du mapper ? mapper = (Person person) -> person.getAge() ; 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
  43. 43. La façon du JDK 8 Comment le compilateur reconnaît-il l’implémentation du mapper ? mapper = (Person person) -> person.getAge() ; 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
  44. 44. 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
  45. 45. D’autres lambdas Et la plupart du temps, le compilateur reconnaît ceci : mapper = person -> person.getAge() ; // mapper filter = age -> age > 20 ; // filter reducer = (i1, i2) -> i1 + i2 ; // reducer Le type des paramètres peut être omis
  46. 46. Une remarque sur la réduction Comment cela fonctionne-t-il réellement ?
  47. 47. Réduction 2 exemples : Reducer r1 = (i1, i2) -> i1 + i2 ; // Ok Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // Oooops Attention : le résultat est toujours reproductible en série il ne l’est en général pas en parallèle
  48. 48. Pour le moment Une expression lambda est une autre façon d’écrire des instances de classes anonymes
  49. 49. Il y a d’autres syntaxes On peut écrire : mapper = person -> person.getAge() ; Mais on peut aussi écrire : mapper = Person::getAge ; // méthode non statique
  50. 50. Il y a d’autres syntaxes On peut écrire : sum = (i1, i2) -> i1 + i2 ; sum = Integer::sum ; // méthode statique, nouvelle ! Ou encore : max = (i1, i2) -> i1 > i2 ? i1 : i2 ; max = Integer::max ; // méthode statique, nouvelle !
  51. 51. Il y a d’autres syntaxes Encore un autre exemple : toLower = String::toLowerCase ; // !!!! NON NON NON !!!! toLowerFR = String::toLowerCase(Locale.FRANCE) ;
  52. 52. Plus loin sur les lambdas
  53. 53. Questions : Comment modéliser une expression lambda ?
  54. 54. Questions : Comment modéliser une expression lambda ? Puis-je mettre une expression lambda dans une variable ?
  55. 55. Questions : Comment modéliser une expression lambda ? Puis-je mettre une expression lambda dans une variable ? Un lambda est-il un objet ?
  56. 56. Modélisation Un lambda = instance d’une « interface fonctionnelle » @FunctionalInterface public interface Consumer<T> { public void accept(T t) ; }
  57. 57. Modélisation Un lambda = instance d’une « interface fonctionnelle » @FunctionalInterface public interface Consumer<T> { public void accept(T t) ; } - ne possède qu’une unique méthode
  58. 58. Modélisation Un lambda = instance d’une « interface fonctionnelle » @FunctionalInterface public interface Consumer<T> { public void accept(T t) ; } - ne possède qu’une unique méthode - peut être annotée par @FunctionalInterface (optionnel)
  59. 59. 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) ; } } ; Donc : Consumer<String> c = s -> System.out.println(s) ;
  60. 60. 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) ; } } ; Question : qu’est-ce que s ? Donc : Consumer<String> c = s -> System.out.println(s) ;
  61. 61. 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) ; } } ; Donc : Réponse : le compilateur infère qu’il s’agit d’un String Consumer<String> c = s -> System.out.println(s) ;
  62. 62. 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) ; } } ;
  63. 63. 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) ; } } ; JDK 7 : i doit être final
  64. 64. 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) ; } } ; JDK 8 : ce code compile
  65. 65. Mettre un lambda dans une variable En revanche, ce code … int i = ... ; Consumer<String> c = new Consumer<String>() { @Override public void accept(String s) { System.out.println("i = " + i) ; } } ; i = i + 1 ; … ne compile pas
  66. 66. 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
  67. 67. Note au sujet de this JDK 7 : this est l’instance anonyme 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" ; } } ; Appeler accept() affiche donc « je suis dans c »
  68. 68. Note au sujet de this JDK 8 : idem, accept() affiche « 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" ; } } ; Mais …
  69. 69. Note au sujet de this Un consommateur peut aussi s’écrire comme ça : Consumer<String> c = s -> System.out.println(this) ; Dans ce cas, this est l’instance englobante
  70. 70. 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 ?
  71. 71. 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) ;
  72. 72. 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) ;
  73. 73. 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
  74. 74. Des lambdas à profusion : Java.util.functions
  75. 75. Java.util.functions C’est dans ce package que sont les interfaces fonctionnelles Il y en a 43
  76. 76. Java.util.functions Supplier Consumer / BiConsumer Function / BiFunction (UnaryOperator / BinaryOperator) Predicate / BiPredicate En plus des versions construites sur les types primitifs
  77. 77. Supplier Un supplier fournit un objet public interface Supplier<T> { T get() ; }
  78. 78. 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) ;
  79. 79. 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
  80. 80. Function Une fonction prend un objet et retourne un autre objet public interface Function<T, R> { R apply(T t) ; } 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
  81. 81. Predicate Un Predicate prend un objet et retourne un booléen public interface Predicate<T> { boolean test(T t) ; } Il peut être inversé, et composé avec des AND ou OR
  82. 82. BiPredicate Un BiPredicate prend deux objets et retourne un booléen public interface BiPredicate<T, U> { boolean test(T t, U u) ; } Version pour les types booléens
  83. 83. Retour sur map / filter / reduce
  84. 84. La façon JDK 7 Comment implémenter le pattern map / filter / reduce sur List<Person> ?
  85. 85. 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
  86. 86. 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
  87. 87. 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() ) ;
  88. 88. Mapping d’une liste Mapping en JDK 8 avec des lambdas List<Person> persons = new ArrayList<>() ; List<Integer> ages = Lists.map( persons, Person::getAge ) ;
  89. 89. 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) ;
  90. 90. 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) ; L’idée est de pousser un lambda vers l’implémentation, et de la laisser itérer en interne
  91. 91. 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) ; Pour : l’API peut être optimisée sans que l’on ait à toucher au code, super !
  92. 92. 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) ; Pour : l’API peut être optimisée sans que l’on ait à toucher au code, super ! Contre…
  93. 93. 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) ;
  94. 94. 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) ; 2 duplications : ages & agesGT20
  95. 95. 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) ; 2 duplications : ages & agesGT20 Que fait-on de ces listes ? On les envoie au GC !
  96. 96. 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) ;
  97. 97. 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) ; … mais on doit encore gagner du temps !
  98. 98. 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) ; Seule solution : paralléliser !
  99. 99. Pattern complet 2) Supposons que l’on veuille 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) ; Il faut mieux que ages et agesGT20 soient des collections concurrentes !
  100. 100. Pattern complet 3) Supposons que nous aillons un reducer : 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) ; allMatch est vrai si tous les noms sont plus courts que 20 caractères Voyons une implémentation possible de allMatch()
  101. 101. É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 ; }
  102. 102. É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 !
  103. 103. É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…
  104. 104. Pattern complet Quand on applique la réduction allMatch()… // pattern map / filter / reduce List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n.length() < 20) ; … names a déjà été évaluée !
  105. 105. Pattern complet Quand on applique la réduction allMatch()… // pattern map / filter / reduce List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n.length() < 20) ; … names a déjà été évaluée ! On a perdu une belle optimisation…
  106. 106. Pattern complet Quand on applique la réduction allMatch()… // pattern map / filter / reduce List<Person> persons = ... ; List<String> names = Lists.map(persons, p -> p.getName()) ; boolean allMatch = Lists.allMatch(names, n.length() < 20) ; … names a déjà été évaluée ! Il aurait fallu appliquer le mapping de façon lazy
  107. 107. 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) ;
  108. 108. 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) ;
  109. 109. 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) ;
  110. 110. Conclusion (again) On a besoin d’un nouveau concept pour traiter les listes de grande taille de façon efficace
  111. 111. 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
  112. 112. 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 !
  113. 113. Quel pattern choisir ?
  114. 114. Introduction Implémenter le map / filter / reduce sur Collection aurait mené à ceci : // map / filter / reduce pattern sur Collection int sum = persons .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ; Et même si cette approche n’est pas viable, c’est une façon agréable d’écrire les choses
  115. 115. 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) ;
  116. 116. Introduction Collection.stream() retourne un Stream : une nouvelle interface // map / filter / reduce pattern sur Collection int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ; Nouvelle interface = on a les mains libres !
  117. 117. 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() ; }
  118. 118. 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() ; }
  119. 119. 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 !
  120. 120. Nouvelles interfaces
  121. 121. 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…
  122. 122. Interfaces Java 8 Problème : ajouter des méthodes à une interface sans toucher aux implémentations… Solution : changer la façon dont les interfaces fonctionnent en Java !
  123. 123. Interfaces Java 8 ArrayList a besoin de l’implémentation de stream()… public interface Collection<E> { // nos bonnes vieilles méthodes Stream<E> stream() ; }
  124. 124. Interfaces Java 8 Solution : mettons-les dans l’interface ! public interface Collection<E> { // nos bonnes vieilles méthodes default Stream<E> stream() { return ... ; } }
  125. 125. Interfaces Java 8 Introduisons les default methods en Java public interface Collection<E> { // nos bonnes vieilles méthodes default Stream<E> stream() { return ... ; } } Les méthodes par défaut permettent de faire évoluer de vieilles interfaces
  126. 126. Méthodes par défaut Cela amène-t-il l’héritage multiple en Java ?
  127. 127. Méthodes par défaut Cela amène-t-il l’héritage multiple en Java ? Oui, mais on l’a déjà
  128. 128. 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 { // ... }
  129. 129. Méthodes par défaut Ce que l’on a en Java est l’héritage multiple de type
  130. 130. 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
  131. 131. 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
  132. 132. 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 !
  133. 133. Méthodes par défaut Peut-on avoir des conflits ?
  134. 134. Méthodes par défaut Peut-on avoir des conflits ? Hélas oui…
  135. 135. Méthodes par défaut Peut-on avoir des conflits ? Hélas oui… Il nous faut donc des règles pour les gérer
  136. 136. Méthodes par défaut public class C implements A, B { // ... }
  137. 137. Méthodes par défaut public class C implements A, B { // ... } public interface A { default String a() { ... } } public interface B { default String a() { ... } }
  138. 138. Méthodes par défaut public class C implements A, B { public String a() { ... } } Exemple #0 public interface A { default String a() { ... } } public interface B { default String a() { ... } } La classe gagne ! L’implémentation efface la méthode par défaut
  139. 139. Méthodes par défaut public class C implements A, B { // ... } Exemple #1 public interface A { default String a() { ... } } public interface B { default String a() { ... } }
  140. 140. Méthodes par défaut public class C implements A, B { // ... } Exemple #1 public interface A { default String a() { ... } } public interface B { default String a() { ... } } Erreur de compilation : class C inherits unrelated defaults for a() from types A and B
  141. 141. Méthodes par défaut public class C implements A, B { // ... } Exemple #2 public interface A extends B { default String a() { ... } } public interface B { default String a() { ... } } A est plus spécifique que B : A.a() a la priorité
  142. 142. Méthodes par défaut public class D extends C implements B { // ... } public class C implements A { // ... } Exemple #2 public interface A extends B { default String a() { ... } } public interface B { default String a() { ... } }
  143. 143. Méthodes par défaut public class D extends C implements B { // ... } public class C implements A { // ... } Exemple #2 public interface A extends B { default String a() { ... } } public interface B { default String a() { ... } } A est toujours plus spécifique que B : A.a() gagne
  144. 144. Méthodes par défaut public class C implements A, B { public String a() { return B.super.a() ; } } Retour sur l’exemple #1 public interface A { default String a() { ... } } public interface B { default String a() { ... } } On peut aussi faire un appel explicite
  145. 145. 2 règles simples Pour gérer les conflits de l’héritage multiple d’implémentation
  146. 146. 2 règles simples Pour gérer les conflits de l’héritage multiple d’implémentation 1) La classe gagne
  147. 147. 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
  148. 148. 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 !") ; } ; }
  149. 149. Un exemple Grâce à Java 8 : public interface Iterator<E> { default void remove() { throw new UnsupportedOperationException("remove") ; } ; } Plus besoin d’écrire ce code !
  150. 150. 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!") ; } ; }
  151. 151. 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!") ; } ; }
  152. 152. 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
  153. 153. 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
  154. 154. L’API Stream
  155. 155. Qu’est-ce qu’un Stream ? D’un point de vue technique : une interface paramétrée « un stream de String »
  156. 156. Qu’est-ce qu’un Stream ? D’un point de vue technique : une interface paramétrée « un stream de String » D’autres interfaces pour les types primitifs : IntStream, LongStream, DoubleStream
  157. 157. 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
  158. 158. 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 »
  159. 159. 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
  160. 160. 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
  161. 161. 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
  162. 162. Une nouvelle notion 1) 2) 3) 4) Un stream ne porte pas de donnée Un stream ne peut pas modifier sa source Une source peut être infinie, ou non bornée 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
  163. 163. Un Stream est-il une Collection ? La réponse est non
  164. 164. Un Stream est-il une Collection ? La réponse est non Une collection permet d’itérer sur ses éléments
  165. 165. Un Stream est-il une Collection ? La réponse est non Une collection permet d’itérer sur ses éléments Pas un Stream
  166. 166. Un Stream est-il une Collection ? La réponse est non Une collection ne permet pas d’appliquer un lambda sur ses éléments
  167. 167. 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
  168. 168. Comment construire un Stream ? De nombreuses façons de faire… 1) À partir d’une collection : Collection<String> collection = ... ; Stream<String> stream = collection.stream() ;
  169. 169. 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"}) ;
  170. 170. 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") ;
  171. 171. 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) ;
  172. 172. 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
  173. 173. Un premier exemple Retour sur notre map / filter / reduce Construction d’un collections Stream sur une List // map / filter / reduce pattern on int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  174. 174. Un premier exemple Retour sur notre map / filter / reduce Déclarations // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  175. 175. Un premier exemple Retour sur notre map / filter / reduce On lance le collections calcul // map / filter / reduce pattern on int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  176. 176. 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
  177. 177. 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
  178. 178. 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
  179. 179. 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)
  180. 180. Un Stream possède un état Certaines opérations changent cet état - le filtrage annule SIZED - le mapping annule DISTINCT et SORTED
  181. 181. Un Stream possède un état Et cela permet d’optimiser ! - un HashSet ne peut pas avoir de doublons…
  182. 182. 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
  183. 183. 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…
  184. 184. 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
  185. 185. Un ème 2 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()) ;
  186. 186. Un ème 2 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()) ;
  187. 187. Un ème 2 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()) ; Dans ce cas le code de tri n’est plus exécuté !
  188. 188. Opérations stateless / statefull Opérations Stateless / statefull Certaines opérations sont stateless : persons.stream().map(p -> p.getAge()) ; Cela signifie que l’on n’a pas besoin de plus d’information que ce qui est contenu dans p
  189. 189. Opérations stateless / statefull Opérations Stateless / statefull Certaines opérations sont stateless : persons.stream().map(p -> p.getAge()) ; Pas le cas de toutes les opérations stream.limit(10_000_000) ; // select les premiers 10M éléments
  190. 190. 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()) ; }
  191. 191. 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()) ; } Soooo Java 7…
  192. 192. Exemple Trions un tableau de String Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(rand.nextLong()) ) ; Meilleur ?
  193. 193. Exemple Trions un tableau de String // Random rand = new Random() ; Stream<String> stream = Stream.generate( () -> Long.toHexString(ThreadLocalRandom.current().nextLong()) ) ; Meilleur !
  194. 194. Exemple Trions un tableau de String // other way Stream<String> stream = ThreadLocalRandom .current() .longs() // returns a LongStream .mapToObj(l -> Long.toHexString(l)) ;
  195. 195. Exemple Trions un tableau de String // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) ;
  196. 196. 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
  197. 197. 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() ;
  198. 198. 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
  199. 199. Example Trions un tableau de String // other way Stream<String> stream = ThreadLocalRandom .current() .longs() .mapToObj(Long::toHexString) .limit(10_000_000) .sorted() ; Opérations intermédiares ! Object [] sorted = stream.toArray() ; T = 14 s
  200. 200. 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
  201. 201. 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
  202. 202. Parallel Streams
  203. 203. 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
  204. 204. 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
  205. 205. Construction d’un Stream parallèle Deux patterns 1) Appeler parallelStream() au lieu de stream() Stream<String> s = strings.parallelStream() ; 2) Appeler parallel() sur un stream existant Stream<String> s = strings.stream().parallel() ;
  206. 206. Peut-on choisir le nombre cœurs ? La réponse est non
  207. 207. Peut-on choisir le nombre cœurs ? La réponse est non Mais… en a-t-on vraiment besoin ?
  208. 208. Paralléliser est-il aussi simple ? En fait oui !
  209. 209. Paralléliser est-il aussi simple ? En fait oui ! … et non…
  210. 210. Paralléliser est-il aussi simple ? Exemple 1 : persons.stream().limit(10_000_000) ; « retourne les premiers 10M éléments »
  211. 211. Paralléliser est-il aussi simple ? Exemple 1 : persons.parallelStream().limit(10_000_000) ; « retourne les premiers 10M éléments »
  212. 212. Paralléliser est-il aussi simple ? Exemple 1 : persons.parallelStream().limit(10_000_000) ; « retourne les premiers 10M éléments » Comment peut-on tracer que les éléments sont les premiers sur un CPU multicœur ?
  213. 213. 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()) ; }
  214. 214. 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()) ;
  215. 215. 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()) ;
  216. 216. Paralléliser est-il aussi simple ? Exemple 1 : performances Code 1 (for) Code 2 (limit) Code 3 (longs) Série 270 ms 310 ms 250 ms Parallèle
  217. 217. Paralléliser est-il aussi simple ? Exemple 1 : performances Code 1 (for) Code 2 (limit) Code 3 (longs) Série 270 ms 310 ms 250 ms Parallèle 500 ms 320 ms
  218. 218. Paralléliser est-il aussi simple ? Exemple 2 : Stream s3 = Stream.concat(stream1, stream2) ; « retourne un Stream composé des éléments du premier Stream, puis des éléments du second »
  219. 219. 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
  220. 220. Réductions
  221. 221. 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 »
  222. 222. 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 »
  223. 223. 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) ;
  224. 224. Une réduction simple Ca serait bien de pouvoir écrire : // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ; Mais sum() n’est pas définie sur Stream<T> Que voudrait dire « additionner des personnes » ?
  225. 225. Une réduction simple Ca serait bien de pouvoir écrire : // map / filter / reduce pattern on collections int sum = persons.stream() .map(p -> p.getAge()) .filter(a -> a > 20) .sum() ; Mais sum() n’est pas définie sur Stream<T> Mais il y a une méthode sum() sur IntStream !
  226. 226. Une réduction simple 2ème version : // map / filter / reduce pattern on collections int sum = persons.stream() .map(Person::getAge) .filter(a -> a > 20) .mapToInt(Integer::intValue) .sum() ; Résultat pour une liste vide : 0
  227. 227. Une réduction simple 2ème version (encore meilleure) : // map / filter / reduce pattern on collections int sum = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) // .mapToInt(Integer::intValue) .sum() ; Résultat pour une liste vide : 0
  228. 228. Une réduction simple Qu’en est-il de min() et de max() ? // map / filter / reduce pattern on collections ....... = persons.stream() .mapToInt(Person::getAge) .filter(a -> a > 20) .max() ; Quelle valeur par défaut pour max() ?
  229. 229. Problème des valeurs par défaut La notion de « valeur par défaut » est plus complexe qu’il y paraît…
  230. 230. 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
  231. 231. 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
  232. 232. 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
  233. 233. 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
  234. 234. 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
  235. 235. Problème des valeurs par défaut Donc quelle est la valeur par défaut de max() et min() ?
  236. 236. 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()
  237. 237. 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() ;
  238. 238. 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() ; Si c’est int, la valeur par défaut sera 0…
  239. 239. 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 »
  240. 240. 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 » }
  241. 241. 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() ;
  242. 242. 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) ;
  243. 243. 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) ;
  244. 244. Available Optionals Optional<T> OptionalInt, OptionalLong, OptionalDouble
  245. 245. Réductions disponibles Sur Stream<T> : - reduce() - count(), min(), max() - anyMatch(), allMatch(), noneMatch() - findFirst(), findAny() - toArray() - forEach(), forEachOrdered()
  246. 246. Réductions disponibles Sur IntStream, LongStream, DoubleStream : - average() - summaryStatistics()
  247. 247. Mutable reductions : exemple 1 Utilisation d’une classe helper : Collectors ArrayList<String> strings = stream .map(Object::toString) .collect(Collectors.toList()) ;
  248. 248. Mutable reductions : exemple 2 Concaténation de String avec un helper String names = persons .stream() .map(Person::getName) .collect(Collectors.joining()) ;
  249. 249. 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)
  250. 250. 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>() ;
  251. 251. 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) ;
  252. 252. 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) ;
  253. 253. 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 ) ;
  254. 254. 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 ) ;
  255. 255. Collectors
  256. 256. 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
  257. 257. La classe Collectors Average, Sum, Count persons .stream() .collect(Collectors.averagingDouble(Person::getAge)) ; persons .stream() .collect(Collectors.counting()) ;
  258. 258. La classe Collectors Concaténation des nom dans une String String names = persons .stream() .map(Person::getName) .collect(Collectors.joining(", ")) ;
  259. 259. La classe Collectors Accumulation dans un Set Set<Person> setOfPersons = persons .stream() .collect( Collectors.toSet()) ;
  260. 260. La classe Collectors Accumulation dans une collection passée en paramètre TreeSet<Person> treeSetOfPersons = persons .stream() .collect( Collectors.toCollection(TreeSet::new)) ;
  261. 261. La classe Collectors Calculer un max avec un comparateur Optional<Person> optionalPerson = persons .stream() .collect( Collectors.maxBy( Comparator.comparing(Person::getAge)) ; Bonus : une API Comparator
  262. 262. Construction de comparateurs Nouvelle API pour construire des comparateurs Comparator<Person> comp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person:getAge) ;
  263. 263. 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
  264. 264. Classe Collectors : mapping Accumuler les noms des personnes dans un Set Set<String> set = persons .stream() .collect( Collectors.mapping( Person::getLastName, Collectors.toSet())) ;
  265. 265. Classe Collectors : mapping Mapper le stream, accumulation dans une collection TreeSet<String> set = persons .stream() .collect( Collectors.mapping( Person::getLastName, Collectors.toCollection(TreeSet::new)) ) ;
  266. 266. 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)
  267. 267. Classe Collectors : groupingBy Des personnes par âge Map<Integer, List<Person>> map = persons .stream() .collect( Collectors.groupingBy(Person::getAge)) ;
  268. 268. Classe Collectors : groupingBy … rangées dans des Set Map<Integer, Set<Person>> map = persons .stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.toSet() // le downstream ) ;
  269. 269. 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() // ) ) ;
  270. 270. 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) ) ) ;
  271. 271. 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) ) ) ;
  272. 272. Classe Collectors : groupingBy Exemple : création d’un histogramme des âges Map<Integer, Long> map = persons .stream() .collect( Collectors.groupingBy(Person::getAge, Collectors.counting()) ) ; Donne le nombre de personnes par âge
  273. 273. 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
  274. 274. Classe Collectors : partionningBy Crée une Map<Boolean, …> avec un prédicat Map<Boolean, List<Person>> map = persons .stream() .collect( Collectors.partitioningBy(p -> p.getAge() > 20) ) ; map.get(TRUE) retourne la liste des personnes de plus de 20 ans
  275. 275. 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)) ) ) ) ;
  276. 276. Classe Collectors : collectingAndThen Collecte les données avec un downstream Applique enfin une fonction appelée « finisher » Indispensable pour retourner des collections immutables
  277. 277. Classe Collectors : collectingAndThen 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 ) ; Dans ce cas « Map::entrySet » est un finisher
  278. 278. Quelques exemples réels
  279. 279. er 1 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()) ;
  280. 280. er 1 exemple Un stream de movies 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()) ;
  281. 281. er 1 exemple Construction d’une map année / # de films 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()) ;
  282. 282. er 1 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
  283. 283. er 1 exemple Et déterminer la plus grande valeur 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()) ;
  284. 284. er 1 exemple Retourne l’année qui a vu le plus grand nombre de films 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()) ;
  285. 285. Remarques sur parallel()
  286. 286. 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
  287. 287. 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
  288. 288. 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
  289. 289. Conclusion
  290. 290. Conclusion Pourquoi les lambdas ont-ils été introduits dans Java 8 ?
  291. 291. Conclusion Pourquoi les lambdas ont-ils été introduits dans Java 8 ? Réponse : parce que c’est à la mode !
  292. 292. Conclusion Pourquoi les lambdas ont-ils été introduits dans Java 8 ? Réponse : parce que c’est à la mode !
  293. 293. 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 !
  294. 294. 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
  295. 295. 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
  296. 296. 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 !
  297. 297. 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
  298. 298. Conclusion Java 8 arrive, il s’agit de la mise à jour la plus importante depuis 15 ans que Java existe
  299. 299. 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
  300. 300. 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…
  301. 301. 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
  302. 302. 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
  303. 303. 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
  304. 304. 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 !

×