SlideShare une entreprise Scribd logo
Java 8 Streams & Collectors
Patterns, performance, parallélisation
@JosePaumard
@JosePaumard
@JosePaumard
Stream
@JosePaumard
Stream
Collectors
@JosePaumard
Stream
Collectors
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
@JosePaumard#Stream8
Du code et des slides
1ère partie : des slides
- Stream
- Opérations
- État
- Réduction
- Collector
2ème partie : code + slides
- Houston
- Shakespeare
- Actors
Questions ?
#Stream8
@JosePaumard
Stream
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Techniquement : une interface paramétrée
public interface Stream<T> extends BaseStream<T, Stream<T>> {
// ...
}
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Techniquement : une interface paramétrée
Pratiquement : un nouveau concept
public interface Stream<T> extends BaseStream<T, Stream<T>> {
// ...
}
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
À quoi un Stream sert-il ?
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
À quoi un Stream sert-il ?
Réponse : à traiter efficacement les grands volumes de
données, et aussi les petits
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Que signifie efficacement ?
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Que signifie efficacement ?
Deux choses :
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Que signifie efficacement ?
Deux choses :
1) en parallèle, pour exploiter le multicœur
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Que signifie efficacement ?
Deux choses :
1) en parallèle, pour exploiter le multicœur
2) en pipeline, pour éviter les intermédiaires de calcul
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Pourquoi une collection ne peut-elle constituer un Stream ?
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Pourquoi une collection ne peut-elle constituer un Stream ?
Il y a des arguments pour qu’une collection soit un Stream !
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Pourquoi une collection ne peut-elle constituer un Stream ?
Il y a des arguments pour qu’une collection soit un Stream !
1) mes données sont dans des collections (ou tables de
hachage)
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Pourquoi une collection ne peut-elle constituer un Stream ?
Il y a des arguments pour qu’une collection soit un Stream !
1) mes données sont dans des collections (ou tables de
hachage)
2) on connaît bien l’API Collection…
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Pourquoi une collection ne peut-elle constituer un Stream ?
Deux raisons :
1) ça permet d’avoir les mains libres
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Pourquoi une collection ne peut-elle constituer un Stream ?
Deux raisons :
1) ça permet d’avoir les mains libres
2) ça permet de ne pas polluer l’API Collection avec ces
nouveaux concepts
@JosePaumard#Stream8
Donc : qu’est-ce qu’un Stream ?
Réponses :
@JosePaumard#Stream8
Donc : qu’est-ce qu’un Stream ?
Réponses :
1) un objet qui sert à définir des opérations
@JosePaumard#Stream8
Donc : qu’est-ce qu’un Stream ?
Réponses :
1) un objet qui sert à définir des opérations
2) qui ne possède pas les données qu’il traite (source)
@JosePaumard#Stream8
Donc : qu’est-ce qu’un Stream ?
Réponses :
1) un objet qui sert à définir des opérations
2) qui ne possède pas les données qu’il traite (source)
3) qui s’interdit de modifier les données qu’il traite
@JosePaumard#Stream8
Donc : qu’est-ce qu’un Stream ?
Réponses :
1) un objet qui sert à définir des opérations
2) qui ne possède pas les données qu’il traite (source)
3) qui s’interdit de modifier les données qu’il traite
4) qui traite les données en « une passe »
@JosePaumard#Stream8
Donc : qu’est-ce qu’un Stream ?
Réponses :
1) un objet qui sert à définir des opérations
2) qui ne possède pas les données qu’il traite (source)
3) qui s’interdit de modifier les données qu’il traite
4) qui traite les données en « une passe »
5) qui est optimisé du point de vue algorithmique et qui est
capable de calculer en parallèle
@JosePaumard#Stream8
Comment construit-on un Stream ?
Plein de patterns !
@JosePaumard#Stream8
Comment construit-on un Stream ?
Plein de patterns !
Choisissons-en un :
List<Person> persons = ... ;
Stream<Person> stream = persons.stream() ;
@JosePaumard#Stream8
Opérations sur un Stream
Première opération : forEach()
… affiche chaque personne sur la console
List<Person> persons = ... ;
Stream<Person> stream = persons.stream() ;
stream.forEach(p -> System.out.println(p)) ;
@JosePaumard#Stream8
Opérations sur un Stream
Première opération : forEach()
… affiche chaque personne sur la console
List<Person> persons = ... ;
Stream<Person> stream = persons.stream() ;
stream.forEach(System.out::println) ;
@JosePaumard#Stream8
Opérations sur un Stream
Première opération : forEach()
Method reference :
List<Person> persons = ... ;
Stream<Person> stream = persons.stream() ;
stream.forEach(System.out::println) ;
o -> System.out.println(o) ≡ System.out::println
@JosePaumard#Stream8
Opération forEach()
Première opération : forEach()
forEach() : prend un Consumer<T> en paramètre
@FunctionalInterface
public interface Consumer<T> {
void accept(T t) ;
}
@JosePaumard#Stream8
Opération forEach()
Première opération : forEach()
forEach() : prend un Consumer<T> en paramètre
Interface fonctionnelle ?
@FunctionalInterface
public interface Consumer<T> {
void accept(T t) ;
}
@JosePaumard#Stream8
Sauf qu’en fait…
Consumer est un peu plus complexe que ça :
@FunctionalInterface
public interface Consumer<T> {
void accept(T t) ;
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
@JosePaumard#Stream8
Sauf qu’en fait…
Consumer est un peu plus complexe que ça :
Méthode par défaut ?
@FunctionalInterface
public interface Consumer<T> {
void accept(T t) ;
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
@JosePaumard#Stream8
Interface fonctionnelle
= une interface qui ne possède qu’une seule méthode
@JosePaumard#Stream8
Interface fonctionnelle
= une interface qui ne possède qu’une seule méthode
« peut » être annotée par @FunctionalInterface
@JosePaumard#Stream8
Interface fonctionnelle
= une interface qui ne possède qu’une seule méthode
« peut » être annotée par @FunctionalInterface
Les méthodes de Object ne « comptent » pas
@JosePaumard#Stream8
Interface fonctionnelle
= une interface qui ne possède qu’une seule méthode
« peut » être annotée par @FunctionalInterface
Les méthodes de Object ne « comptent » pas
Peut comporter des méthodes « par défaut »
@JosePaumard#Stream8
Méthodes par défaut
Nouveauté Java 8 !
@JosePaumard#Stream8
Méthodes par défaut
Nouveauté Java 8 !
Intégrée pour permettre de faire évoluer des vieilles
interfaces
@JosePaumard#Stream8
Méthodes par défaut
Nouveauté Java 8 !
Intégrée pour permettre de faire évoluer des vieilles
interfaces
Cas de Collection : on a ajouté stream()
@JosePaumard#Stream8
Méthodes par défaut
Quid de l’héritage multiple ?
@JosePaumard#Stream8
Méthodes par défaut
Quid de l’héritage multiple ?
On a déjà l’héritage multiple en Java !
public final class String
implements Serializable, Comparable<String>, CharSequence {
// ...
}
@JosePaumard#Stream8
Méthodes par défaut
Quid de l’héritage multiple ?
On a déjà l’héritage multiple en Java !
On a l’héritage multiple de type
public final class String
implements Serializable, Comparable<String>, CharSequence {
// ...
}
@JosePaumard#Stream8
Méthodes par défaut
Java 8 amène l’héritage multiple d’implémentation
@JosePaumard#Stream8
Méthodes par défaut
Java 8 amène l’héritage multiple d’implémentation
Ce que l’on n’a pas, c’est l’héritage multiple d’état
@JosePaumard#Stream8
Méthodes par défaut
Java 8 amène l’héritage multiple d’implémentation
Ce que l’on n’a pas, c’est l’héritage multiple d’état
et d’ailleurs… on n’en veut pas !
@JosePaumard#Stream8
Méthodes par défaut
Conflits ?
@JosePaumard#Stream8
Méthodes par défaut
Conflits ?
oui…
@JosePaumard#Stream8
Méthodes par défaut
public class A
implements B, C {
}
public interface C {
default String a() {...}
}
public interface B {
default String a() {...}
}
@JosePaumard#Stream8
Méthodes par défaut
Conflit : erreur de compilation !
public class A
implements B, C {
}
public interface C {
default String a() {...}
}
public interface B {
default String a() {...}
}
@JosePaumard#Stream8
Méthodes par défaut
Pour lever l’erreur : deux solutions
public class A
implements B, C {
}
public interface C {
default String a() {...}
}
public interface B {
default String a() {...}
}
@JosePaumard#Stream8
Méthodes par défaut
1) La classe gagne !
public class A
implements B, C {
public String a() {...}
}
public interface C {
default String a() {...}
}
public interface B {
default String a() {...}
}
@JosePaumard#Stream8
Méthodes par défaut
1) La classe gagne !
public class A
implements B, C {
public String a() { B.super.a() ; }
}
public interface C {
default String a() {...}
}
public interface B {
default String a() {...}
}
@JosePaumard#Stream8
Méthodes par défaut
2) Le plus spécifique gagne !
public class A
implements B, C {
}
public interface B extends C {
default String a() {...}
}
public interface C {
default String a() {...}
}
@JosePaumard#Stream8
Méthodes par défaut
Conflits ?
oui…
2 règles pour les gérer :
@JosePaumard#Stream8
Méthodes par défaut
Conflits ?
oui…
2 règles pour les gérer :
1) La classe gagne !
@JosePaumard#Stream8
Méthodes par défaut
Conflits ?
oui…
2 règles pour les gérer :
1) La classe gagne !
2) Le plus spécifique gagne !
@JosePaumard#Stream8
Retour sur Consumer
@FunctionalInterface
public interface Consumer<T> {
void accept(T t) ;
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
@JosePaumard#Stream8
Retour sur Consumer
List<String> liste = new ArrayList<>() ;
Consumer<String> c1 = s -> liste.add(s) ;
Consumer<String> c2 = s -> System.out.println(s) ;
@JosePaumard#Stream8
Retour sur Consumer
List<String> liste = new ArrayList<>() ;
Consumer<String> c1 = liste::add ;
Consumer<String> c2 = System.out::println ;
@JosePaumard#Stream8
Retour sur Consumer
List<String> liste = new ArrayList<>() ;
Consumer<String> c1 = liste::add ;
Consumer<String> c2 = System.out::println ;
Consumer<String> c3 = c1.andThen(c2) ; // et on pourrait continuer
@JosePaumard#Stream8
Retour sur Consumer
Attention à la concurrence !
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
Consumer<String> c1 = liste::add ;
persons.stream()
.forEach(c1) ; // concurrence ?
@JosePaumard#Stream8
Retour sur Consumer
Attention à la concurrence !
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
Consumer<String> c1 = liste::add ;
persons.stream().parallel()
.forEach(c1) ; // concurrence ?
@JosePaumard#Stream8
Retour sur Consumer
Attention à la concurrence !
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
Consumer<String> c1 = liste::add ;
persons.stream()
.forEach(c1) ; // concurrence ? Baaad pattern !
@JosePaumard#Stream8
Retour sur Consumer
Problème : forEach() ne retourne rien
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
Consumer<String> c1 = liste::add ;
Consumer<String> c2 = System.out::println
persons.stream()
.forEach(c1.andThen(c2)) ;
@JosePaumard#Stream8
Retour sur Consumer
Peek à la rescousse !
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
persons.stream()
.peek(System.out::println)
.filter(person -> person.getAge() > 20)
.peek(resultat::add) ; // Baaad pattern !
@JosePaumard#Stream8
Retour sur Stream
Donc on a :
- une méthode forEach(Consumer)
- une méthode peek(Consumer)
@JosePaumard#Stream8
Retour sur Stream
3ème méthode : filter(Predicate)
@JosePaumard#Stream8
Méthode filter()
3ème méthode : filter(Predicate)
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<Person> filtered =
stream.filter(person -> person.getAge() > 20) ;
@JosePaumard#Stream8
Méthode filter()
3ème méthode : filter(Predicate)
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<Person> filtered =
stream.filter(person -> person.getAge() > 20) ;
Predicate<Person> p = person -> person.getAge() > 20 ;
@JosePaumard#Stream8
Interface Predicate
Interface fonctionnelle
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t) ;
}
@JosePaumard#Stream8
Interface Predicate
En fait …
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t) ;
default Predicate<T> and(Predicate<? super T> other) { ... }
default Predicate<T> or(Predicate<? super T> other) { ... }
default Predicate<T> negate() { ... }
}
@JosePaumard#Stream8
Interface Predicate
default Predicate<T> and(Predicate<? super T> other) {
return t -> test(t) && other.test(t) ;
}
default Predicate<T> or(Predicate<? super T> other) {
return t -> test(t) || other.test(t) ;
}
default Predicate<T> negate() {
return t -> !test(t) ;
}
@JosePaumard#Stream8
Interface Predicate : patterns
Predicate<Integer> p1 = i -> i > 20 ;
Predicate<Integer> p2 = i -> i < 30 ;
Predicate<Integer> p3 = i -> i == 0 ;
@JosePaumard#Stream8
Interface Predicate : patterns
Predicate<Integer> p1 = i -> i > 20 ;
Predicate<Integer> p2 = i -> i < 30 ;
Predicate<Integer> p3 = i -> i == 0 ;
Predicate<Integer> p = p1.and(p2).or(p3) ; // (p1 AND p2) OR p3
@JosePaumard#Stream8
Interface Predicate : patterns
Predicate<Integer> p1 = i -> i > 20 ;
Predicate<Integer> p2 = i -> i < 30 ;
Predicate<Integer> p3 = i -> i == 0 ;
Predicate<Integer> p = p1.and(p2).or(p3) ; // (p1 AND p2) OR p3
Predicate<Integer> p = p3.or(p1).and(p2) ; // (p3 OR p1) AND p2
@JosePaumard#Stream8
Interface Predicate
En fait (bis) …
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t) ;
// méthodes par défaut
static <T> Predicate<T> isEqual(Object o) { ... }
}
@JosePaumard#Stream8
Interface Predicate
static <T> Predicate<T> isEqual(Object o) {
return t -> o.equals(t) ;
}
@JosePaumard#Stream8
Interface Predicate
En fait :
static <T> Predicate<T> isEqual(Object o) {
return t -> o.equals(t) ;
}
static <T> Predicate<T> isEqual(Object o) {
return (null == o)
? obj -> Objects.isNull(obj)
: t -> o.equals(t) ;
}
@JosePaumard#Stream8
Interface Predicate
En fait :
static <T> Predicate<T> isEqual(Object o) {
return t -> o.equals(t) ;
}
static <T> Predicate<T> isEqual(Object o) {
return (null == o)
? Objects::isNull
: o::equals ;
}
@JosePaumard#Stream8
Interface Predicate : patterns
Predicate<String> p = Predicate.isEqual("deux") ;
@JosePaumard#Stream8
Interface Predicate : patterns
Predicate<String> p = Predicate.isEqual("deux") ;
Stream<String> stream1 = Stream.of("un", "deux", "trois") ;
Stream<String> stream2 = stream1.filter(p) ;
@JosePaumard#Stream8
Interface Predicate : remarque
Dans ce code :
Les deux streams stream et filtered sont différents
La méthode filter() retourne un nouvel objet
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<Person> filtered =
stream.filter(person -> person.getAge() > 20) ;
@JosePaumard#Stream8
Interface Predicate : remarque
Question : qu’est-ce que j’ai dans cet objet ?
@JosePaumard#Stream8
Interface Predicate : remarque
Question : qu’est-ce que j’ai dans cet objet ?
Réponse : mes données filtrées
@JosePaumard#Stream8
Interface Predicate : remarque
Question : qu’est-ce que j’ai dans cet objet ?
Réponse : mes données filtrées
vraiment ?
@JosePaumard#Stream8
Interface Predicate : remarque
Question : qu’est-ce que j’ai dans cet objet ?
Réponse : mes données filtrées
vraiment ?
« un stream ne porte pas de données »…
@JosePaumard#Stream8
Interface Predicate : remarque
Question : qu’est-ce que j’ai dans cet objet ?
Réponse : mes données filtrées
@JosePaumard#Stream8
Interface Predicate : remarque
Question : qu’est-ce que j’ai dans cet objet ?
Réponse : mes données filtrées
Réponse : rien !
@JosePaumard#Stream8
Interface Predicate : remarque
Question 2 :
que se passe-t-il lors de l’exécution de ce code ?
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<Person> filtered =
stream.filter(person -> person.getAge() > 20) ;
@JosePaumard#Stream8
Interface Predicate : remarque
Question 2 :
que se passe-t-il lors de l’exécution de ce code ?
Réponse : rien !
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<Person> filtered =
stream.filter(person -> person.getAge() > 20) ;
@JosePaumard#Stream8
Interface Predicate : remarque
Question 2 :
que se passe-t-il lors de l’exécution de ce code ?
Réponse : rien !
Cet appel est une déclaration : aucun traitement n’est fait !
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<Person> filtered =
stream.filter(person -> person.getAge() > 20) ;
@JosePaumard#Stream8
Interface Predicate : remarque
L’appel à filter() est un appel lazy
D’une façon générale :
un appel qui retourne un Stream est lazy
@JosePaumard#Stream8
Interface Predicate : remarque
L’appel à filter() est un appel lazy
Autre façon de le dire :
une opération qui retourne un Stream est
une opération intermédiaire
@JosePaumard#Stream8
Appels intermédiaires
Le fait de choisir qu’un stream ne porte pas ses données
crée la notion d’opération intermédiaire
@JosePaumard#Stream8
Appels intermédiaires
Le fait de choisir qu’un stream ne porte pas ses données
crée la notion d’opération intermédiaire
On doit bien avoir des opérations terminales quelque part…
@JosePaumard#Stream8
Retour sur Consumer (bis)
Que se passe-t-il dans ce code ?
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
persons.stream()
.peek(System.out::println)
.filter(person -> person.getAge() > 20)
.peek(resultat::add) ; // Baaad pattern !
@JosePaumard#Stream8
Retour sur Consumer (bis)
Que se passe-t-il dans ce code ?
Rien !
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
persons.stream()
.peek(System.out::println)
.filter(person -> person.getAge() > 20)
.peek(resultat::add) ; // Baaad pattern !
@JosePaumard#Stream8
Retour sur Consumer (bis)
Que se passe-t-il dans ce code ?
1) rien ne s’affiche !
2) resultat reste vide !
List<String> resultat = new ArrayList<>() ;
List<Person> persons = ... ;
persons.stream()
.peek(System.out::println)
.filter(person -> person.getAge() > 20)
.peek(resultat::add) ; // Baaad pattern !
@JosePaumard#Stream8
Retour sur Stream
Donc on a :
- une méthode forEach(Consumer)
- une méthode peek(Consumer)
- une méthode filter(Predicate)
@JosePaumard#Stream8
Continuons
Opération de mapping
@JosePaumard#Stream8
Continuons
Opération de mapping
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<String> names =
stream.map(person -> person.getNom()) ;
@JosePaumard#Stream8
Continuons
Opération de mapping
Retourne un stream (différent du stream original)
Donc opération intermédiaire
List<Person> liste = ... ;
Stream<Person> stream = liste.stream() ;
Stream<String> names =
stream.map(person -> person.getNom()) ;
@JosePaumard#Stream8
Mapper : interface Function
Interface fonctionnelle
@FunctionalInterface
public interface Function<T, R> {
R apply(T t) ;
}
@JosePaumard#Stream8
Mapper : interface Function
En fait
@FunctionalInterface
public interface Function<T, R> {
R apply(T t) ;
default <V> Function<V, R> compose(Function<V, T> before) ;
default <V> Function<T, V> andThen(Function<R, V> after) ;
}
@JosePaumard#Stream8
Mapper : interface Function
En fait
Gare aux génériques !
@FunctionalInterface
public interface Function<T, R> {
R apply(T t) ;
default <V> Function<V, R> compose(Function<V, T> before) ;
default <V> Function<T, V> andThen(Function<R, V> after) ;
}
@JosePaumard#Stream8
Mapper : interface Function
En fait
@FunctionalInterface
public interface Function<T, R> {
R apply(T t) ;
default <V> Function<V, R> compose(
Function<? super V, ? extends T> before) ;
default <V> Function<T, V> andThen(
Function<? super R, ? extends V> after) ;
}
@JosePaumard#Stream8
Mapper : interface Function
En fait
@FunctionalInterface
public interface Function<T, R> {
R apply(T t) ;
// méthodes par défaut
static <T> Function<T, T> identity() {
return t -> t ;
}
}
@JosePaumard#Stream8
Retour sur Stream
Donc on a :
- une méthode forEach(Consumer)
- une méthode peek(Consumer)
- une méthode filter(Predicate)
- une méthode map(Function)
@JosePaumard#Stream8
Continuons
Opération flatMap
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
<R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
<R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ;
<R> Stream<R> map(Function<T, R> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
La fonction mapper transforme
des éléments T
en éléments Stream<R>
... flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
S’il s’agissait d’un mapping classique
flatMap() retournerait Stream<Stream<R>>
... flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
S’il s’agissait d’un mapping classique
flatMap() retournerait Stream<Stream<R>>
Donc un « stream de streams »
... flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
Mais il s’agit d’un flatMap !
... flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
Mais il s’agit d’un flatMap !
Et le flatMap met le Stream<Stream> « à plat »
... flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
Mais il s’agit d’un flatMap !
Et le flatMap met le Stream<Stream> « à plat »
<R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Méthode flatMap()
flatMap() = mettre à plat
Signature :
Retourne un stream, donc opération intermédiaire
<R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ;
@JosePaumard#Stream8
Bilan sur Stream
On a 3 types de méthodes :
- forEach() : consomme
- peek() : regarde et passe à un consommateur
- filter() : filtre
- map() : mappe = transforme
- flatMap() : mappe et met à plat
@JosePaumard#Stream8
Bilan sur Stream
On a 3 types de méthodes :
- forEach() : consomme
- peek() : regarde et passe à un consommateur
- filter() : filtre
- map() : mappe = transforme
- flatMap() : mappe et met à plat
Réduction ?
@JosePaumard#Stream8
Réduction
Méthode de réduction :
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
Integer somme =
stream.reduce(0, (age1, age2) -> age1 + age2) ;
@JosePaumard#Stream8
Réduction
Méthode de réduction :
Expression lambda sur deux éléments : applicable à tout un
stream
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
Integer somme =
stream.reduce(0, (age1, age2) -> age1 + age2) ;
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
@JosePaumard#Stream8
Réduction
Doit être associative…
Reducer r1 = (i1, i2) -> i1 + i2 ; // ok
@JosePaumard#Stream8
Réduction
Doit être associative…
Reducer r1 = (i1, i2) -> i1 + i2 ; // ok
Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // oooops...
@JosePaumard#Stream8
Réduction
Doit être associative…
Reducer r1 = (i1, i2) -> i1 + i2 ; // ok
Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // oooops...
Reducer r3 = (i1, i2) -> (i1 + i2)/2 ; // re-oooops...
@JosePaumard#Stream8
Réduction
Méthode de réduction :
Expression lambda sur deux éléments : applicable à tout un
stream
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
Integer somme =
stream.reduce(0, (age1, age2) -> age1 + age2) ;
@JosePaumard#Stream8
Réduction
Méthode de réduction :
0 : valeur par défaut pour le cas où la source est vide
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
Integer somme =
stream.reduce(0, (age1, age2) -> age1 + age2) ;
@JosePaumard#Stream8
Réduction
Autre méthode de réduction :
Quel type de retour pour max ? (ou min…)
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
... max =
stream.max(Comparator.naturalOrder()) ;
@JosePaumard#Stream8
Type de retour pour la réduction
Quel est la valeur par défaut du max ?
1) la valeur par défaut est la réduction de l’ensemble vide
2) mais c’est aussi l’élément neutre de la réduction
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est l’élément neutre pour le max ?
0 ?
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est l’élément neutre pour le max ?
0 ?
max(0, -1) n’est pas égal à 0
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est l’élément neutre pour le max ?
0 ?
max(0, -1) n’est pas égal à 0
-∞ ?
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est l’élément neutre pour le max ?
0 ?
max(0, -1) n’est pas égal à 0
-∞ ?
souci : ce n’est pas un entier
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est l’élément neutre pour le max ?
0 ?
max(0, -1) n’est pas égal à 0
-∞ ?
souci : ce n’est pas un entier
Integer.MIN_VALUE ?
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est l’élément neutre pour le max ?
0 ?
max(0, -1) n’est pas égal à 0
-∞ ?
souci : ce n’est pas un entier
Integer.MIN_VALUE ?
souci pour convertir en long, et vice-versa
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est l’élément neutre pour le max ?
Réponse : il n’y a pas d’élément neutre pour le max
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est la valeur par défaut pour le max ?
@JosePaumard#Stream8
Type de retour pour la réduction
Problème : quel est la valeur par défaut pour le max ?
Réponse : il n’y a pas de valeur par défaut pour le max
@JosePaumard#Stream8
Type de retour pour la réduction
Quelle est alors le type de retour pour max() ?
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
... max =
stream.max(Comparator.naturalOrder()) ;
@JosePaumard#Stream8
Type de retour pour la réduction
Quelle est alors le type de retour pour max() ?
Si on prend int, alors la valeur par défaut est 0
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
... max =
stream.max(Comparator.naturalOrder()) ;
@JosePaumard#Stream8
Type de retour pour la réduction
Quelle est alors le type de retour pour max() ?
Si on prend int, alors la valeur par défaut est 0
Si on prend Integer, alors la valeur par défaut est null
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
... max =
stream.max(Comparator.naturalOrder()) ;
@JosePaumard#Stream8
Type de retour pour la réduction
Quelle est alors le type de retour pour max() ?
On a besoin d’un nouveau concept : Optional
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
Optional<Integer> max =
stream.max(Comparator.naturalOrder()) ;
@JosePaumard#Stream8
Optional
Un optional encapsule un objet
Peut être vide
Optional<String> opt = ... ;
if (opt.isPresent()) {
String s = opt.get() ;
} else {
...
}
@JosePaumard#Stream8
Optional
Un optional encapsule un objet
Peut être vide
Optional<String> opt = ... ;
if (opt.isPresent()) {
String s = opt.get() ;
} else {
...
}
String s = opt.orElse("") ; // défini une valeur par défaut
// applicative
@JosePaumard#Stream8
Optional
Un optional encapsule un objet
Peut être vide
Optional<String> opt = ... ;
if (opt.isPresent()) {
String s = opt.get() ;
} else {
...
}
String s = opt.orElseThrow(MyException::new) ; // construction lazy
@JosePaumard#Stream8
Retour sur la réduction
Méthodes de réduction :
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
Integer somme =
stream.reduce(0, (age1, age2) -> age1 + age2) ;
List<Integer> ages = ... ;
Stream<Integer> stream = ages.stream() ;
Optional<Integer> opt =
stream.reduce((age1, age2) -> age1 + age2) ;
@JosePaumard#Stream8
Remarque sur la réduction
Une réduction ne retourne pas de Stream :
- max(), min()
- count()
Réduction booléennes :
- allMatch(), noneMatch, anyMatch()
Retourne un Optional
- findFirst(), findAny() (si le Stream est vide ?)
@JosePaumard#Stream8
Remarque sur la réduction
Dans tous les cas, ces réductions retournent une valeur
Elles ne peuvent donc pas être évaluées de façon lazy
Elles déclenchent les opérations !
Ce sont les opérations terminales
@JosePaumard#Stream8
Opération terminale
List<Person> persons = ... ;
... =
persons.map(person -> person.getAge()) // retourne Stream<Integer>
.filter(age -> age > 20) // retourne Stream<Integer>
.min(Comparator.naturalOrder()) ;
@JosePaumard#Stream8
Opération terminale
Écriture d’un map / filter / reduce
List<Person> persons = ... ;
Optional<Integer> age =
persons.map(person -> person.getAge()) // retourne Stream<Integer>
.filter(age -> age > 20) // retourne Stream<Integer>
.min(Comparator.naturalOrder()) ; // opération terminale
@JosePaumard#Stream8
Opération terminale
Écriture d’un map / filter / reduce
List<Person> persons = ... ;
persons.map(person -> person.getLastName())
.allMatch(length < 20) ; // opération terminale
@JosePaumard#Stream8
Opération terminale
Écriture d’un map / filter / reduce
Intérêt de tout faire dans la même boucle
List<Person> persons = ... ;
persons.map(person -> person.getLastName())
.allMatch(length < 20) ; // opération terminale
@JosePaumard#Stream8
Qu’est-ce qu’un Stream ?
Un objet qui permet de définir des traitements sur des jeux
de données arbitrairement grands
Typiquement : map / filter / reduce
Approche pipeline :
1) on définit des opérations
2) on lance les opérations
@JosePaumard#Stream8
Comment un Stream est-il fait ?
Que se passe-t-il lors de la construction d’un Stream ?
List<Person> persons = new ArrayList<>() ;
Stream<Person> stream = persons.stream() ;
// interface Collection
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
@JosePaumard#Stream8
Comment un Stream est-il fait ?
Que se passe-t-il lors de la construction d’un Stream ?
// classe StreamSupport
public static <T> Stream<T> stream(
Spliterator<T> spliterator, boolean parallel) {
Objects.requireNonNull(spliterator) ;
return new ReferencePipeline.Head<>(
spliterator,
StreamOpFlag.fromCharacteristics(spliterator),
parallel) ;
}
@JosePaumard#Stream8
Comment un Stream est-il fait ?
Que se passe-t-il lors de la construction d’un Stream ?
// classe ArrayList
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
@JosePaumard#Stream8
Comment un Stream est-il fait ?
Que se passe-t-il lors de la construction d’un Stream ?
Le Spliterator encapsule la logique d’accès aux données
Celui d’ArrayList manipule le tableau
// classe ArrayList
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
@JosePaumard#Stream8
Fonction du Spliterator
Méthodes à implémenter
Consomme le prochain élément s’il existe
// interface Spliterator
boolean tryAdvance(Consumer<? super T> action);
@JosePaumard#Stream8
Fonction du Spliterator
Méthodes à implémenter
Utilisée par le parallélisme : divise les données en deux,
suivant des règles précises
// interface Spliterator
Spliterator<T> trySplit();
@JosePaumard#Stream8
Fonction du Spliterator
Méthodes à implémenter
Retourne une estimation du nombre d’éléments de ce
Stream
// interface Spliterator
long estimateSize();
@JosePaumard#Stream8
Fonction du Spliterator
Méthodes par défaut
// interface Spliterator
default void forEachRemaining(Consumer<? super T> action) {
do { } while (tryAdvance(action));
}
// interface Spliterator
default long getExactSizeIfKnown() {
return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
}
@JosePaumard#Stream8
Fonction du Spliterator
Méthodes à implémenter
Implémentations
// interface Spliterator
int characteristics();
// pour ArrayList
public int characteristics() {
return Spliterator.ORDERED |
Spliterator.SIZED |
Spliterator.SUBSIZED;
}
@JosePaumard#Stream8
Fonction du Spliterator
Méthodes à implémenter
Implémentations
// interface Spliterator
int characteristics();
// pour HashSet
public int characteristics() {
return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
Spliterator.DISTINCT;
}
@JosePaumard#Stream8
Caractéristiques d’un Stream
Un Stream porte des caractéristiques
Caractéristique
ORDERED L’ordre a un sens
DISTINCT Pas de doublon
SORTED Trié
SIZED Le cardinal est connu
NONNULL Pas de valeurs nulles
IMMUTABLE Ne peut pas être modifié
CONCURRENT Autorise le parallélisme
SUBSIZED Le cardinal est connu
@JosePaumard#Stream8
Caractéristiques d’un Stream
Les opérations changent la valeur des caractéristiques
Méthode Mets à 0 Mets à 1
Filter SIZED -
Map DISTINCT, SORTED -
FlatMap DISTINCT, SORTED, SIZED -
Sorted - SORTED, ORDERED
Distinct - DISTINCT
Limit SIZED -
Peek - -
Unordered ORDERED -
@JosePaumard#Stream8
Caractéristiques d’un Stream
Les caractéristiques d’un Stream sont prise en compte à la
consommation
// HashSet
HashSet<String> strings = ... ;
strings.stream()
.distinct() // ne déclenche pas de traitement
.sorted()
.collect(Collectors.toList()) ;
@JosePaumard#Stream8
Implémentations d’un Stream
Complexe
Partagé en deux :
1) partie algorithmique, on n’a pas envie d’y toucher
2) partie accès aux données : faite pour être surchargée !
@JosePaumard#Stream8
Apparté sur les Comparator
On a écrit :
// interface Comparator
Comparator cmp = Comparator.naturalOrder() ;
@JosePaumard#Stream8
Apparté sur les Comparator
On a écrit :
// interface Comparator
Comparator cmp = Comparator.naturalOrder() ;
// interface Comparator
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>>
Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
@JosePaumard#Stream8
Apparté sur les Comparator
// classe Comparators
enum NaturalOrderComparator
implements Comparator<Comparable<Object>> {
INSTANCE;
public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c1.compareTo(c2);
}
public Comparator<Comparable<Object>> reversed() {
return Comparator.reverseOrder();
}
}
@JosePaumard#Stream8
Apparté sur les Comparator
On peut écrire :
// interface Comparator
Comparator cmp =
Comparator.comparing(Person::getLastName)
.thenComparing(Person::getFirstName)
.thenComparing(Person::getAge) ;
@JosePaumard#Stream8
Apparté sur les Comparator
Méthode comparing()
// interface Comparator
public static
<T, U> Comparator<T>
comparing(Function<T, U> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return
(c1, c2) ->
keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
@JosePaumard#Stream8
Apparté sur les Comparator
Méthode comparing()
// interface Comparator
public static
<T, U extends Comparable<U>> Comparator<T>
comparing(Function<T, U> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T>)
(c1, c2) ->
keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
@JosePaumard#Stream8
Apparté sur les Comparator
Méthode comparing()
// interface Comparator
public static
<T, U extends Comparable<? super U>> Comparator<T>
comparing(Function<? super T, ? extends U> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) ->
keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
@JosePaumard#Stream8
Apparté sur les Comparator
Méthode thenComparing()
// interface Comparator
default
<U> Comparator<T>
thenComparing(Function<T, U> keyExtractor) {
return thenComparing(comparing(keyExtractor));
}
@JosePaumard#Stream8
Apparté sur les Comparator
Méthode thenComparing()
// interface Comparator
default
<U extends Comparable<? super U>> Comparator<T>
thenComparing(Function<? super T, ? extends U> keyExtractor) {
return thenComparing(comparing(keyExtractor));
}
@JosePaumard#Stream8
Apparté sur les Comparator
Méthode thenComparing()
// interface Comparator
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
@JosePaumard#Stream8
Petit bilan
API Stream
- opérations intermédiaires
- opérations terminales
- implémentations à deux niveaux
@JosePaumard#Stream8
Opérations stateless / stateful
Le code suivant :
… sélectionne les 1000 premières personnes
ArrayList<Person> persons = ... ;
Stream<Persons> stream = persons.limit(1_000) ;
@JosePaumard#Stream8
Opérations stateless / stateful
Le code suivant :
… sélectionne les 1000 premières personnes
Cette opération a besoin d’un compteur
Parallélisme ?
ArrayList<Person> persons = ... ;
Stream<Persons> stream = persons.limit(1_000) ;
@JosePaumard#Stream8
Opérations stateless / stateful
Le code suivant :
ArrayList<Person> persons = ... ;
List<String> names =
persons.map(Person::getLastName)
.collect(Collectors.toList()) ;
@JosePaumard#Stream8
Opérations stateless / stateful
Le code suivant :
« les noms des personnes apparaissent dans le même ordre
que les personnes »
ArrayList<Person> persons = ... ;
List<String> names =
persons.map(Person::getLastName)
.collect(Collectors.toList()) ;
@JosePaumard#Stream8
Opérations stateless / stateful
Le code suivant :
« les noms des personnes apparaissent dans le même ordre
que les personnes »
Parallélisme ?
ArrayList<Person> persons = ... ;
List<String> names =
persons.map(Person::getLastName)
.collect(Collectors.toList()) ;
@JosePaumard#Stream8
Opérations stateless / stateful
Le code suivant :
« les noms des personnes apparaissent dans le même ordre
que les personnes »
Parallélisme ?
ArrayList<Person> persons = ... ;
List<String> names =
persons.map(Person::getLastName)
.unordered()
.collect(Collectors.toList()) ;
@JosePaumard#Stream8
Bilan sur les Stream
Un Stream a un état
On peut définir des opérations sur un Stream :
- intermédiaires & terminales
- stateless & stateful
Les traitements sur un Stream peuvent être parallélisés
@JosePaumard#Stream8
Stream & performance
Deux éléments :
- traitements lazy
- traitements parallèles
@JosePaumard#Stream8
Stream & performance
Deux éléments :
- traitements lazy
- traitements parallèles
Stream<T> versus IntStream, LongStream, DoubleStream
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream()
.map(Person::getAge)
.filter(age -> age > 20)
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream() // Stream<Person>
.map(Person::getAge)
.filter(age -> age > 20)
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream() // Stream<Person>
.map(Person::getAge) // Stream<Integer> boxing 
.filter(age -> age > 20)
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream() // Stream<Person>
.map(Person::getAge) // Stream<Integer> boxing 
.filter(age -> age > 20) // Stream<Integer> re-boxing re-
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream() // Stream<Person>
.map(Person::getAge) // Stream<Integer> boxing 
.filter(age -> age > 20) // Stream<Integer> re-boxing re-
.sum() ; // pas de méthode sum() sur Stream<T>
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream() // Stream<Person>
.map(Person::getAge) // Stream<Integer> boxing 
.filter(age -> age > 20) // Stream<Integer> re-boxing re-
.mapToInt(age -> age.getValue()) // IntStream 
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream() // Stream<Person>
.map(Person::getAge) // Stream<Integer> boxing 
.filter(age -> age > 20) // Stream<Integer> re-boxing re-
.mapToInt(Integer::getValue) // IntStream 
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
persons.stream() // Stream<Person>
.mapToInt(Person::getAge) // IntStream 
.filter(age -> age > 20) // IntStream 
// .mapToInt(Integer::getValue)
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
int sum =
persons.stream() // Stream<Person>
.mapToInt(Person::getAge) // IntStream 
.filter(age -> age > 20) // IntStream 
// .mapToInt(Integer::getValue)
.sum() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
??? =
persons.stream() // Stream<Person>
.mapToInt(Person::getAge) // IntStream 
.filter(age -> age > 20) // IntStream 
// .mapToInt(Integer::getValue)
.max() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
OptionalInt opt =
persons.stream() // Stream<Person>
.mapToInt(Person::getAge) // IntStream 
.filter(age -> age > 20) // IntStream 
// .mapToInt(Integer::getValue)
.max() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
??? =
persons.stream() // Stream<Person>
.mapToInt(Person::getAge) // IntStream 
.filter(age -> age > 20) // IntStream 
// .mapToInt(Integer::getValue)
.average() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
ArrayList<Person> persons = ... ;
OptionalInt opt =
persons.stream() // Stream<Person>
.mapToInt(Person::getAge) // IntStream 
.filter(age -> age > 20) // IntStream 
// .mapToInt(Integer::getValue)
.average() ;
@JosePaumard#Stream8
Stream & performance
Retour sur l’exemple
Calcule en une passe : count, sum, min, max
Et donc aussi average
ArrayList<Person> persons = ... ;
IntSummaryStatistics stats =
persons.stream()
.mapToInt(Person::getAge)
.filter(age -> age > 20)
.summaryStatistics() ;
@JosePaumard#Stream8
Parallélisation
Construction d’un Stream parallèle
Construit sur un Fork / Join construit au niveau de la JVM
ArrayList<Person> persons = ... ;
Stream<Person> stream1 = persons.parallelStream() ;
Stream<Person> stream2 = persons.stream().parallel() ;
@JosePaumard#Stream8
Parallélisation
Construction d’un Stream parallèle
Construit sur un Fork / Join construit au niveau de la JVM
ArrayList<Person> persons = ... ;
Stream<Person> stream1 = persons.parallelStream() ;
Stream<Person> stream2 = persons.stream().parallel() ;
@JosePaumard#Stream8
Parallélisation
On peut imposer deux choses :
- le nombre de cœurs sur lequel le FJ Pool va s’installer
- Le FJ Pool lui-même !
@JosePaumard#Stream8
Parallélisation
Par défaut les ForkJoinTask s’exécutent dans le « common
pool », un FJ Pool créé au niveau de la JVM
Le taux de parallélisme de ce pool est le nombre de cœurs
On peut le fixer :
System.setProperty(
"java.util.concurrent.ForkJoinPool.common.parallelism", 2) ;
@JosePaumard#Stream8
Parallélisation
On peut aussi imposer le FJ Pool dans lequel les traitements
sont faits
List<Person> persons = ... ;
ForkJoinPool fjp = new ForkJoinPool(2) ;
fjp.submit(
() -> //
persons.stream().parallel() // implémentation de
.mapToInt(p -> p.getAge()) // Callable<Integer>
.filter(age -> age > 20) //
.average() //
).get() ;
@JosePaumard#Stream8
Réduction
Généralisons la réduction
@JosePaumard#Stream8
Réduction
Généralisons la réduction
Bien sûr, une réduction peut être vue comme une agrégation
au sens SQL (sum, min, max, avg, etc…)
@JosePaumard#Stream8
Réduction
Généralisons la réduction
Bien sûr, une réduction peut être vue comme une agrégation
au sens SQL (sum, min, max, avg, etc…)
On peut voir les choses de façon plus générale
@JosePaumard#Stream8
Réduction
Exemple : la somme
@JosePaumard#Stream8
Réduction
Exemple : la somme
Un « container » résultat : un entier, de valeur initiale 0
@JosePaumard#Stream8
Réduction
Exemple : la somme
Un « container » résultat : un entier, de valeur initiale 0
Une opération « ajouter » : ajoute un élément au container
Une opération « combiner » : fusionner deux containers
utilisée si l’on parallélise le traitement
@JosePaumard#Stream8
Réduction
Remarque sur la valeur initiale
@JosePaumard#Stream8
Réduction
Remarque sur la valeur initiale
Il s’agira de la valeur retournée en cas de réduction sur un
ensemble vide !
@JosePaumard#Stream8
Réduction
Remarque sur la valeur initiale
Il s’agira de la valeur retournée en cas de réduction sur un
ensemble vide !
… c’est donc l’élément neutre de la réduction …
@JosePaumard#Stream8
Réduction
Remarque sur la valeur initiale
Certaines réduction ont donc un problème :
- max,min
- average
@JosePaumard#Stream8
Réduction
Donc une réduction peut se définir par trois opérations :
- Création d’un container résultat
- Ajout d’un élément à un container
- Fusion de deux container
@JosePaumard#Stream8
Réduction
Modélisation, trois opérations :
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
@JosePaumard#Stream8
Réduction
Exemple 1 :
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
() -> new StringBuffer() ;
@JosePaumard#Stream8
Réduction
Exemple 1 :
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
() -> new StringBuffer() ;
(StringBuffer sb, String s) -> sb.append(s) ;
@JosePaumard#Stream8
Réduction
Exemple 1 :
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
() -> new StringBuffer() ;
(StringBuffer sb, String s) -> sb.append(s) ;
(StringBuffer sb1, StringBuffer sb2) -> sb1.append(sb2) ;
@JosePaumard#Stream8
Réduction
Exemple 1 : concaténation de chaîne de caractères
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
StringBuffer::new
StringBuffer::append
StringBuffer::append
@JosePaumard#Stream8
Réduction : mise en œuvre
Exemple 1 : accumulation dans un StringBuffer
List<Person> persons = ... ;
StringBuffer result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
StringBuffer::new, // constructor
StringBuffer::append, // accumulator
StringBuffer::append // combiner
) ;
@JosePaumard#Stream8
Réduction : collectors
Exemple 1 : utilisation d’un collector
List<Person> persons = ... ;
String result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
Collectors.joining()
) ;
@JosePaumard#Stream8
Réduction
Exemple 2 :
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
() -> new ArrayList() ;
@JosePaumard#Stream8
Réduction
Exemple 2 :
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
() -> new ArrayList() ;
(ArrayList list, E e) -> list.add(e) ;
@JosePaumard#Stream8
Réduction
Exemple 2 :
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
() -> new ArrayList() ;
(ArrayList list, E e) -> list.add(e) ;
(ArrayList list1, ArrayList list2) -> list1.addAll(list2) ;
@JosePaumard#Stream8
Réduction
Exemple 2 : accumulation dans une liste
- constructeur : Supplier
- accumulateur : Function
- combiner : Function
ArrayList::new
ArrayList::add
ArrayList::addAll
@JosePaumard#Stream8
Réduction : mise en œuvre
Exemple 2 : accumulation dans une liste
List<Person> persons = ... ;
ArrayList<String> names =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
ArrayList::new, // constructor
ArrayList::add, // accumulator
ArrayList::addAll // combiner
) ;
@JosePaumard#Stream8
Réduction : mise en œuvre
Exemple 2 : accumulation dans une liste
List<Person> persons = ... ;
ArrayList<String> names =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
ArrayList::new, // constructor
Collection::add, // accumulator
Collection::addAll // combiner
) ;
@JosePaumard#Stream8
Réduction : mise en œuvre
Exemple 2 : accumulation dans une liste
List<Person> persons = ... ;
HashSet<String> names =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
HashSet::new, // constructor
Collection::add, // accumulator
Collection::addAll // combiner
) ;
@JosePaumard#Stream8
Réduction : collectors
Exemple 1 : utilisation d’un collector
List<Person> persons = ... ;
List<String> result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
Collectors.toList()
) ;
@JosePaumard#Stream8
Réduction : collectors
Exemple 1 : utilisation d’un collector
List<Person> persons = ... ;
Set<String> result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
Collectors.toSet()
) ;
@JosePaumard#Stream8
Réduction : collectors
Exemple 1 : utilisation d’un collector
List<Person> persons = ... ;
TreeSet<String> result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
Collectors.toCollection(TreeSet::new)
) ;
Collector
@JosePaumard#Stream8
Réduction : collectors
Exemple 1 : utilisation d’un collector
List<Person> persons = ... ;
String result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
Collectors.joining(", ")
) ;
@JosePaumard#Stream8
Réduction : collectors
Exemple 2 : utilisation d’un collector
List<Person> persons = ... ;
List<String> result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
Collectors.toList()
) ;
@JosePaumard#Stream8
Réduction : collectors
Exemple 2 : utilisation d’un collector
List<Person> persons = ... ;
Set<String> result =
persons.stream()
.filter(person -> person.getAge() > 20)
.map(Person::getLastName)
.collect(
Collectors.toSet()
) ;
@JosePaumard#Stream8
Collectors
Classe Collectors : 33 méthodes statiques
Boite à outils !
@JosePaumard#Stream8
Collectors : groupingBy
Table de hachage : age / liste des personnes
List<Person> persons = ... ;
Map<Integer, List<Person>> result =
persons.stream()
.collect(
Collectors.groupingBy(Person::getAge)
) ;
@JosePaumard#Stream8
Collectors : groupingBy
Table de hachage : age / nombre de personnes
Map<Integer, Long> result =
persons.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.counting() // « downstream collector »
)
) ;
@JosePaumard#Stream8
Collectors : groupingBy
Table de hachage : age / liste des noms des personnes
Map<Integer, List<String>> result =
persons.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.mapping( //
Person::getLastName // downstream collector
) //
)
) ;
@JosePaumard#Stream8
Collectors : groupingBy
Table de hachage : age / liste des noms des personnes
Map<Integer, String> result =
persons.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.mapping( // 1er downstream collector
Person::getLastName
Collectors.joining(", ") // 2ème downstream collector
)
)
) ;
@JosePaumard#Stream8
Collectors : groupingBy
Table de hachage : age / liste des noms des personnes
Map<Integer, TreeSet<String>> result =
persons.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.mapping(
Person::getLastName
Collectors.toCollection(TreeSet::new)
)
)
) ;
@JosePaumard#Stream8
Collectors : groupingBy
Table de hachage : age / liste des noms des personnes
TreeMap<Integer, TreeSet<String>> result =
persons.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
TreeMap::new,
Collectors.mapping(
Person::getLastName
Collectors.toCollection(TreeSet::new)
)
)
) ;
@JosePaumard#Stream8
Bilan intermédiaire
Stream + Collectors =
Nouveaux outils pour le map / filter / reduce
1) exécutions lazy
2) exécutions parallèle
Des exemples à venir…
… mais pour le moment …
@JosePaumard
Coffee time !
#Stream8
Java 8 Streams & Collectors
Patterns, performance, parallélisation
@JosePaumard
#Stream8
Cas d’utilisation
« Houston, we have a problem »
https://github.com/JosePaumard/jdk8-lambda-tour
@JosePaumard#Stream8
Collectors : groupingBy
Fichier CSV contenant les MacDo US
-149.95038,61.13712,"McDonalds-Anchorage,AK","3828 W Dimond Blvd, Anchorage,AK, (907) 248-0597"
-149.93538,61.18167,"McDonalds-Anchorage,AK","4350 Spenard Rd, Anchorage,AK, (907) 243-5122"
http://introcs.cs.princeton.edu/java/data/
@JosePaumard#Stream8
Collectors : groupingBy
Fichier CSV contenant les MacDo US
-149.95038,61.13712,"McDonalds-Anchorage,AK","3828 W Dimond Blvd, Anchorage,AK, (907) 248-0597"
-149.93538,61.18167,"McDonalds-Anchorage,AK","4350 Spenard Rd, Anchorage,AK, (907) 243-5122"
try (Stream<String> lines =
Files.lines(Paths.get("files", "mcdonalds.csv"))) {
...
}
@JosePaumard#Stream8
McDonald : compter les villes
1ère solution
long nTowns =
mcdos.stream()
.map(McDonald::city) // mcdo -> mcdo.city()
@JosePaumard#Stream8
McDonald : compter les villes
1ère solution
long nTowns =
mcdos.stream()
.map(McDonald::city) // mcdo -> mcdo.city()
.collect(Collectors.toSet())
@JosePaumard#Stream8
McDonald : compter les villes
1ère solution
long nTowns =
mcdos.stream()
.map(McDonald::city) // mcdo -> mcdo.city()
.collect(Collectors.toSet())
.size() ;
@JosePaumard#Stream8
McDonald : compter les villes
2ème solution :
long nTowns =
mcdos.stream()
.map(McDonald::city) // mcdo -> mcdo.city()
.distinct()
.collect(Collectors.counting()) ;
@JosePaumard#Stream8
McDonald : compter les villes
3ème solution :
long nTowns =
mcdos.stream()
.map(McDonald::city) // mcdo -> mcdo.city()
.distinct()
.count() ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
1ère étape : histogramme ville / # de mcdos
C’est une table de hachage !
Map<String, Long> map =
mcdos.stream()
.collect(
Collectors.groupingBy( // table de hachage
)
) ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
1ère étape : histogramme ville / # de mcdos
C’est une table de hachage !
Map<String, Long> map =
mcdos.stream()
.collect(
Collectors.groupingBy(
McDonald::city, // les clés sont les villes
)
) ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
1ère étape : histogramme ville / # de mcdos
C’est une table de hachage !
Map<String, Long> map =
mcdos.stream()
.collect(
Collectors.groupingBy(
McDonald::city,
Collectors.counting() // downstream
)
) ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
Il s’agit d’un max
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
Il s’agit d’un max
- Sur quel ensemble ?
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
Il s’agit d’un max
- Sur quel ensemble ?
- Au sens de quel comparateur ?
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
Il s’agit d’un max
- Sur quel ensemble ?
- L’ensemble des paires clés / valeurs
- Au sens de quel comparateur ?
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
Il s’agit d’un max
- Sur quel ensemble ?
- L’ensemble des paires clés / valeurs
- Au sens de quel comparateur ?
- Le comparateur compare les valeurs
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
map.entrySet() // Set des paires clés / valeurs
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
map.entrySet() // Set des paires clés / valeurs
.stream()
.max( // on prend le max
) ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
map.entrySet() // Set des paires clés / valeurs
.stream()
.max( // on prend le max
Comparator.comparing(entry -> entry.getValue())
) ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
map.entrySet() // Set des paires clés / valeurs
.stream()
.max( // on prend le max
Comparator.comparing(Map.Entry::getValue)
) ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
map.entrySet() // Set des paires clés / valeurs
.stream()
.max( // on prend le max
Map.Entry.comparingByValue() // comparingByKey()
) ;
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
2ème étape : extraire la clé associée à la plus grande valeur
Optional<Map.Entry<String, Long>> opt =
map.entrySet() // Set des paires clés / valeurs
.stream()
.max( // on prend le max
Map.Entry.comparingByValue() // comparingByKey()
) ;
@JosePaumard#Stream8
McDonald : n villes qui en ont le plus
Plus compliqué…
1) Trier les paires clés / valeurs
@JosePaumard#Stream8
McDonald : n villes qui en ont le plus
Plus compliqué…
1) Trier les paires clés / valeurs
2) Prendre les n plus grandes
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
Tri de l’ensemble des paires clés / valeurs
map.entrySet() // Set des paires clés / valeurs
.stream()
.sorted( // tri
Comparator.comparing(entry -> -entry.getValue())
)
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
Tri de l’ensemble des paires clés / valeurs
map.entrySet() // Set des paires clés / valeurs
.stream()
.sorted( // tri
Comparator.comparing(entry -> -entry.getValue())
)
.limit(n) // n : nombre de valeurs que l’on veut
@JosePaumard#Stream8
McDonald : la ville qui en a le plus
Tri de l’ensemble des paires clés / valeurs
List<Map.Entry<String, Long>> result =
map.entrySet() // Set des paires clés / valeurs
.stream()
.sorted( // tri
Comparator.comparing(entry -> -entry.getValue())
)
.limit(n) // n : nombre de valeurs que l’on veut
.collect(Collectors.toList()) ;
Cas d’utilisation
« Shakespeare joue au Scrabble »
https://github.com/JosePaumard/jdk8-lambda-tour
@JosePaumard#Stream8
Scrabble
Deux fichiers :
- les mots autorisés au Scrabble
- les mots utilisés par Shakespeare
@JosePaumard#Stream8
Scrabble
Deux fichiers :
- les mots autorisés au Scrabble
- les mots utilisés par Shakespeare
Un peu de doc (Wikipedia) :
private static final int [] scrabbleENScore = {
// a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10} ;
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
B O N J O U R
3 1 1 8 1 1 1 // lecture du tableau scrabbleENScore
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
B O N J O U R
3 1 1 8 1 1 1 // lecture du tableau scrabbleENScore
SUM
= 16
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
B O N J O U R // stream des lettres du mot « bonjour »
3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre
SUM
= 16
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
B O N J O U R // stream des lettres du mot « bonjour »
3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre
sum()
= 16
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
B O N J O U R // stream des lettres du mot « bonjour »
3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre
sum()
= 16
word -> word.chars() // stream des lettres du mot
.map(lettre -> scrabbleENScore[lettre – 'a'])
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
B O N J O U R // stream des lettres du mot « bonjour »
3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre
sum()
= 16
word -> word.chars() // stream des lettres du mot, type IntStream 
.map(lettre -> scrabbleENScore[lettre – 'a'])
.sum() ; // existe sur IntStream
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
B O N J O U R // stream des lettres du mot « bonjour »
3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre
sum()
= 16
Function<String, Integer> score =
word -> word.chars() // stream des lettres du mot, type IntStream 
.map(lettre -> scrabbleENScore[lettre – 'a'])
.sum() ; // existe sur IntStream
@JosePaumard#Stream8
Scrabble : score d’un mot
1ère question : calculer le score d’un mot
= map / reduce
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
1) Histogramme des mots en fonction de leur score
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
1) Histogramme des mots en fonction de leur score
2) max sur les clés
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
1) Histogramme des mots en fonction de leur score
 table de hachage : groupingBy
2) max sur les clés
 on sait faire
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
shakespeareWords.stream()
.collect(
Collectors.groupingBy(
score // fonction de calcul de score
)
)
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
Map<Integer, List<String>> map =
shakespeareWords.stream()
.collect(
Collectors.groupingBy(
score // fonction de calcul de score
)
) ;
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
shakespeareWords.stream()
.collect(
Collectors.groupingBy(
score
) // Map<Integer, List<String>>
)
.entrySet()
.stream() // Map.Entry<Integer, List<String>>
.max(Map.Entry.comparingByKey()) ;
@JosePaumard#Stream8
Scrabble : score de Shakespeare
2ème question : calculer le meilleur score de Shakespeare
Optional<Map.Entry<Integer, List<String>> opt =
shakespeareWords.stream()
.collect(
Collectors.groupingBy(
score
) // Map<Integer, List<String>>
)
.entrySet()
.stream() // Map.Entry<Integer, List<String>>
.max(Map.Entry.comparingByKey()) ;
@JosePaumard#Stream8
Scrabble : score de Shakespeare
3ème question : limiter aux mots autorisés au Scrabble
Optional<Map.Entry<Integer, List<String>> opt =
shakespeareWords.stream()
.filter(word -> scrabbleWords.contains(word))
.collect(
Collectors.groupingBy(
score
) // Map<Integer, List<String>>
)
.entrySet()
.stream() // Map.Entry<Integer, List<String>>
.max(Map.Entry.comparingByKey()) ;
@JosePaumard#Stream8
Scrabble : score de Shakespeare
3ème question : limiter aux mots autorisés au Scrabble
Optional<Map.Entry<Integer, List<String>> opt =
shakespeareWords.stream()
.filter(scrabbleWords::contains)
.collect(
Collectors.groupingBy(
score
) // Map<Integer, List<String>>
)
.entrySet()
.stream() // Map.Entry<Integer, List<String>>
.max(Map.Entry.comparingByKey()) ;
@JosePaumard#Stream8
Scrabble : score de Shakespeare
3ème question : limiter aux mots autorisés au Scrabble
= problème de filter / reduce
@JosePaumard#Stream8
Scrabble : score de Shakespeare
Question : le mot en question est-il possible au Scrabble ?
@JosePaumard#Stream8
Scrabble : score de Shakespeare
Question : le mot en question est-il possible au Scrabble ?
Il n’y a qu’un seul ‘Z’ au Scrabble anglais…
private static final int [] scrabbleENDistribution = {
// a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
9, 2, 2, 1, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1} ;
@JosePaumard#Stream8
Scrabble : score de Shakespeare
Question : le mot en question est-il possible au Scrabble ?
Il n’y a qu’un seul ‘Z’ au Scrabble anglais…
Il faut vérifier le nombre d’utilisation de chaque lettre
private static final int [] scrabbleENDistribution = {
// a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
9, 2, 2, 1, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1} ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
B U Z Z A R D // le mot
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre -> groupingBy
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
T T F T T T // il faut que tout soit à TRUE -> allMatch
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
word -> word.chars() // IntStream 
.mapToObj(Integer::new) // Stream<Integer>
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
word -> word.chars() // IntStream 
.mapToObj(Integer::new) // Stream<Integer>
.collect(
Collectors.groupingBy(
)
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
word -> word.chars() // IntStream 
.mapToObj(Integer::new) // Stream<Integer>
.collect( // Map<Integer, Long>
Collectors.groupingBy(
letter -> letter,
Collectors.counting()
)
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
word -> word.chars() // IntStream 
.mapToObj(Integer::new) // Stream<Integer>
.collect( // Map<Integer, Long>
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
Function<String, Map<Integer, Long>> letterHisto =
word -> word.chars() // IntStream 
.mapToObj(Integer::new) // Stream<Integer>
.collect( // Map<Integer, Long>
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
Predicate<String> canWrite =
word -> letterHisto
.apply(word) // Map<Integer, Long>
.entrySet()
.stream() // Map.Entry<Integer, Long>
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
Predicate<String> canWrite =
word -> letterHisto
.apply(word) // Map<Integer, Long>
.entrySet()
.stream() // Map.Entry<Integer, Long>
.allMatch(
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
Predicate<String> canWrite =
word -> letterHisto
.apply(word) // Map<Integer, Long>
.entrySet()
.stream() // Map.Entry<Integer, Long>
.allMatch(
entry ->
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
Predicate<String> canWrite =
word -> letterHisto
.apply(word) // Map<Integer, Long>
.entrySet()
.stream() // Map.Entry<Integer, Long>
.allMatch(
entry ->
entry.getValue() <= // besoin en lettres
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
Predicate<String> canWrite =
word -> letterHisto
.apply(word) // Map<Integer, Long>
.entrySet()
.stream() // Map.Entry<Integer, Long>
.allMatch(
entry ->
entry.getValue() <= // besoin en lettres
scrabbleENDistrib[entry.getKey() – 'a'] // lettres dispos
) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
Optional<Map.Entry<Integer, List<String>> opt =
shakespeareWords.stream()
.filter(scrabbleWords::contains)
.filter(canWrite)
.collect(
Collectors.groupingBy(
score
) // Map<Integer, List<String>>
)
.entrySet()
.stream() // Map.Entry<Integer, List<String>>
.max(Map.Entry.comparingByKey()) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
4ème question : peut-on écrire ce mot ?
= problème de map / reduce
@JosePaumard#Stream8
Scrabble : et si l’on prend les blancs ?
5ème question : et si l’on utilise les blancs disponibles ?
@JosePaumard#Stream8
Scrabble : et si l’on prend les blancs ?
5ème question : et si l’on utilise les blancs disponibles ?
Deux impacts :
@JosePaumard#Stream8
Scrabble : et si l’on prend les blancs ?
5ème question : et si l’on utilise les blancs disponibles ?
Deux impacts :
- impact sur les mots que l’on peut faire
@JosePaumard#Stream8
Scrabble : et si l’on prend les blancs ?
5ème question : et si l’on utilise les blancs disponibles ?
Deux impacts :
- impact sur les mots que l’on peut faire
- impact sur le calcul du score
@JosePaumard#Stream8
Scrabble : et si l’on prend les blancs ?
5ème question : et si l’on utilise les blancs disponibles ?
Comment déterminer si un mot est accepté ?
@JosePaumard#Stream8
Scrabble : et si l’on prend les blancs ?
5ème question : et si l’on utilise les blancs disponibles ?
Comment déterminer si un mot est accepté ?
Critère : pas plus de deux blancs
@JosePaumard#Stream8
Scrabble : et si l’on prend les blancs ?
5ème question : et si l’on utilise les blancs disponibles ?
Comment déterminer si un mot est accepté ?
Critère : pas plus de deux blancs
On peut écrire une fonction qui calcule le nombre de blancs
utilisés pour un mot
@JosePaumard#Stream8
Scrabble : mot possible ?
5ème question : et si l’on utilise les blancs disponibles ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
@JosePaumard#Stream8
Scrabble : mot possible ?
5ème question : et si l’on utilise les blancs disponibles ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
0 0 1 0 0 0 // nombre de blancs nécessaires
// il s’agit d’un max !
@JosePaumard#Stream8
Scrabble : mot possible ?
5ème question : et si l’on utilise les blancs disponibles ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
0 0 1 0 0 0 // nombre de blancs nécessaires
// il s’agit d’un max !
Integer.max(
0,
entry.getValue() - // lettres nécessaires
scrabbleEnDistrib[entry.getKey() – 'a'] // lettres dispo
@JosePaumard#Stream8
Scrabble : mot possible ?
5ème question : et si l’on utilise les blancs disponibles ?
Function<String, Integer> nBlanks =
word -> letterHisto.apply(word)
.entrySet()
.stream()
.mapToInt( // IntStream -> sum() dispo
Integer.max(
0,
entry.getValue() -
scrabbleEnDistrib[entry.getKey() – 'a']
)
)
.sum() ;
@JosePaumard#Stream8
Scrabble : mot possible ?
5ème question : et si l’on utilise les blancs disponibles ?
Optional<Map.Entry<Integer, List<String>> opt =
shakespeareWords.stream()
.filter(scrabbleWords::contains)
.filter(word -> nBlanks.apply(word) <= 2)
.collect(
Collectors.groupingBy(
score
)
)
.entrySet()
.stream()
.max(Map.Entry.comparingByKey()) ;
@JosePaumard#Stream8
Scrabble : mot possible ?
5ème question : et si l’on utilise les blancs disponibles ?
= problème de map / reduce
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
= problème de map / reduce
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
1 1 1 1 1 1 // lettres effectivement utilisées
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
1 1 1 1 1 1 // lettres effectivement utilisées
// il s’agit d’un min !
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
1 1 1 1 1 1 // lettres effectivement utilisées
* * * * * *
3 1 10 1 1 2 // score individuel de chaque lettre
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
B U Z Z A R D // le mot
B U Z A R D // les lettres utilisées
1 1 2 1 1 1 // l’occurrence de chaque lettre
2 4 1 9 6 1 // les lettres disponibles
1 1 1 1 1 1 // lettres effectivement utilisées
* * * * * *
3 1 10 1 1 2 // score individuel de chaque lettre
SUM
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
Function<String, Integer> score =
word -> letterHisto.apply(word)
.entrySet()
.stream()
.mapToInt(
.sum() ;
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
Function<String, Integer> score =
word -> letterHisto.apply(word)
.entrySet()
.stream()
.mapToInt(
entry ->
.sum() ;
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
Function<String, Integer> score =
word -> letterHisto.apply(word)
.entrySet()
.stream()
.mapToInt(
entry ->
scrabbleENScore[entry.getKey() – 'a']* // score de
// la lettre
.sum() ;
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
Function<String, Integer> score =
word -> letterHisto.apply(word)
.entrySet()
.stream()
.mapToInt(
entry ->
scrabbleENScore[entry.getKey() – 'a']*
Integer.min(
)
.sum() ;
@JosePaumard#Stream8
Scrabble : score avec les blancs ?
6ème question : compter les points avec les blancs ?
Function<String, Integer> score =
word -> letterHisto.apply(word)
.entrySet()
.stream()
.mapToInt(
entry ->
scrabbleENScore[entry.getKey() – 'a']*
Integer.min(
entry.getValue(), // nombre de lettres dans le mot
scrabbleENDistrib[entry.getKey() – 'a']
)
.sum() ;
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
6 solutions pour poser le mot :
- utilisation de la case de droite = 3 lettres de la fin du mot
- utilisation de la case de gauche = 3 lettres du début
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
Solution :
- prendre le max des 3 dernières lettres
- avec le max des 3 premières lettres
…si le mot fait 7 lettres !
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
Traduit en Java, le max est pris sur que ensemble ?
word.chars().skip(4) // 1er flux
word.chars().limit(Integer.max(0, word.length() - 4)) // 2ème flux
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
Traduit en Java, le max est pris sur que ensemble ?
Le max de ces deux flux, puis le max des ces deux max
word.chars().skip(4) // 1er flux
word.chars().limit(Integer.max(0, word.length() - 4)) // 2ème flux
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
Traduit en Java, le max est pris sur que ensemble ?
Concaténer ces flux ?
word.chars().skip(4) // 1er flux
word.chars().limit(Integer.max(0, word.length() - 4)) // 2ème flux
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
IntStream.concat(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.map(scrabbleENScore)
.max()
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : le « lettre compte double » ?
Problème : concat ne se parallélise pas bien…
IntStream.concat(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.map(scrabbleENScore)
.max()
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
) // retourne un Stream de Stream !
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity())
.map(scrabbleENScore)
.max()
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
Que faire de cet Optional ?
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity())
.map(scrabbleENScore)
.max()
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
Que faire de cet Optional ? … qui peut être vide !
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity())
.map(scrabbleENScore)
.max()
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity())
.map(scrabbleENScore)
.max()
.ifPresent( )
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
List<Integer> list = new ArrayList<>() ;
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity())
.map(scrabbleENScore)
.max()
.ifPresent(list::add)
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
List<Integer> list = new ArrayList<>() ;
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity())
.map(scrabbleENScore)
.max()
.ifPresent(list::add) ;
list.stream().max(Comparator.naturalOrder()).get()
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
List<Integer> list = new ArrayList<>() ;
list.add(0) ;
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity())
.map(scrabbleENScore)
.max()
.ifPresent(list::add) ;
list.stream().max(Comparator.naturalOrder()).get()
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
Function<String, Integer> scoreAtPosition =
word -> {
List<Integer> list = new ArrayList<>() ;
list.add(0) ;
Stream.of(
word.chars().skip(4)
word.chars().limit(Integer.max(0, word.length() - 4))
)
.flatMap(Function.identity()).map(scrabbleENScore).max()
.ifPresent(list::add) ;
return list.stream().max(Comparator.naturalOrder()).get()
}
@JosePaumard#Stream8
Scrabble : et sur le plateau de jeu ?
7ème question : « lettre compte double » ?
= problème de map / reduce !
Cas d’utilisation
« des films et des acteurs »
https://github.com/JosePaumard/jdk8-lambda-tour
@JosePaumard#Stream8
Des films et des acteurs
Fichier : « movies-mpaa.txt »
Contient 14k films américains de 1916 à 2004
Avec :
- le titre
- l’année de sortie
- la liste des acteurs
@JosePaumard#Stream8
Des films et des acteurs
Fichier : « movies-mpaa.txt »
Contient 14k films américains de 1916 à 2004
Avec :
- le titre
- l‘année de sortie
- la liste des acteurs
Référence 170k acteurs différents
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
Construire une table de hachage :
- les clés sont les acteurs
- les valeurs sont le nombre de films
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
Construction de l’ensemble des acteurs
Set<Actor> actors =
movies.stream()
.flatMap(movie -> movie.actors().stream())
.collect(Collector.toSet()) ;
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
actors.stream()
.collect(
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
actors.stream()
.collect(
Collectors.toMap(
)
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
actors.stream()
.collect(
Collectors.toMap(
Function.identity(),
)
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
actors.stream()
.collect(
Collectors.toMap(
Function.identity(),
actor -> movies.stream()
.filter(movie -> movie.actors().contains(actor))
.count()
)
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
actors.stream().parallel() // T = 40s 
.collect(
Collectors.toMap(
Function.identity(),
actor -> movies.stream()
.filter(movie -> movie.actors().contains(actor))
.count()
)
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
movies.stream()
.map(movie -> movie.actors().stream()) // stream de stream !
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
movies.stream()
.flatMap(movie -> movie.actors().stream()) // stream d’acteurs
.collect(
)
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
movies.stream()
.flatMap(movie -> movie.actors().stream()) // stream d’acteurs
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
)
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
movies.stream()
.flatMap(movie -> movie.actors().stream()) // stream d’acteurs
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
@JosePaumard#Stream8
Des films et des acteurs
1ère question : quel acteur a joué dans le plus de films ?
Réponse : Frank Welker
@JosePaumard#Stream8
Des films et des acteurs
2ème question : idem, en une année ?
@JosePaumard#Stream8
Des films et des acteurs
2ème question : idem, en une année ?
movies.stream()
.flatMap(movie -> movie.actors().stream()) // stream d’acteurs
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
2ème question : idem, en une année ?
movies.stream()
.flatMap(movie -> movie.actors().stream()) // stream d’acteurs
.collect(
Collectors.groupingBy(
Function.identity(),
// construire une table de hachage année / nombre
)
)
.entrySet().stream()
.max(Map.Entry.comparingByValue()).get() ;
@JosePaumard#Stream8
Des films et des acteurs
2ème question : idem, en une année ?
On peut construire une table de hachage :
- les clés sont les années
- les valeurs sont … des tables de hachage
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patterns

Contenu connexe

Tendances

L'API Collector dans tous ses états
L'API Collector dans tous ses étatsL'API Collector dans tous ses états
L'API Collector dans tous ses états
José Paumard
 
Being Functional on Reactive Streams with Spring Reactor
Being Functional on Reactive Streams with Spring ReactorBeing Functional on Reactive Streams with Spring Reactor
Being Functional on Reactive Streams with Spring Reactor
Max Huang
 
Java 8 Stream API. A different way to process collections.
Java 8 Stream API. A different way to process collections.Java 8 Stream API. A different way to process collections.
Java 8 Stream API. A different way to process collections.
David Gómez García
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!
Jakub Kubrynski
 
spring-api-rest.pdf
spring-api-rest.pdfspring-api-rest.pdf
spring-api-rest.pdf
Jaouad Assabbour
 
Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]
Aaron Gustafson
 
Java 8 lambda expressions
Java 8 lambda expressionsJava 8 lambda expressions
Java 8 lambda expressions
Logan Chien
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring boot
Antoine Rey
 
Java 8 lambda
Java 8 lambdaJava 8 lambda
Java 8 lambda
Manav Prasad
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentation
Van Huong
 
Spring data jpa
Spring data jpaSpring data jpa
Spring data jpa
Jeevesh Pandey
 
Java11 New Features
Java11 New FeaturesJava11 New Features
Java11 New Features
Haim Michael
 
Introduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examplesIntroduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examples
ecosio GmbH
 
Introduction to Java 8
Introduction to Java 8Introduction to Java 8
Introduction to Java 8
Knoldus Inc.
 
Jsp/Servlet
Jsp/ServletJsp/Servlet
Jsp/Servlet
Sunil OS
 
Java I/O
Java I/OJava I/O
Spring Boot
Spring BootSpring Boot
Spring Boot
Pei-Tang Huang
 
Core java concepts
Core java  conceptsCore java  concepts
Core java concepts
Ram132
 
Java8 features
Java8 featuresJava8 features
Java8 features
Elias Hasnat
 
Java 8 - Features Overview
Java 8 - Features OverviewJava 8 - Features Overview
Java 8 - Features Overview
Sergii Stets
 

Tendances (20)

L'API Collector dans tous ses états
L'API Collector dans tous ses étatsL'API Collector dans tous ses états
L'API Collector dans tous ses états
 
Being Functional on Reactive Streams with Spring Reactor
Being Functional on Reactive Streams with Spring ReactorBeing Functional on Reactive Streams with Spring Reactor
Being Functional on Reactive Streams with Spring Reactor
 
Java 8 Stream API. A different way to process collections.
Java 8 Stream API. A different way to process collections.Java 8 Stream API. A different way to process collections.
Java 8 Stream API. A different way to process collections.
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!
 
spring-api-rest.pdf
spring-api-rest.pdfspring-api-rest.pdf
spring-api-rest.pdf
 
Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]
 
Java 8 lambda expressions
Java 8 lambda expressionsJava 8 lambda expressions
Java 8 lambda expressions
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring boot
 
Java 8 lambda
Java 8 lambdaJava 8 lambda
Java 8 lambda
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentation
 
Spring data jpa
Spring data jpaSpring data jpa
Spring data jpa
 
Java11 New Features
Java11 New FeaturesJava11 New Features
Java11 New Features
 
Introduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examplesIntroduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examples
 
Introduction to Java 8
Introduction to Java 8Introduction to Java 8
Introduction to Java 8
 
Jsp/Servlet
Jsp/ServletJsp/Servlet
Jsp/Servlet
 
Java I/O
Java I/OJava I/O
Java I/O
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Core java concepts
Core java  conceptsCore java  concepts
Core java concepts
 
Java8 features
Java8 featuresJava8 features
Java8 features
 
Java 8 - Features Overview
Java 8 - Features OverviewJava 8 - Features Overview
Java 8 - Features Overview
 

Similaire à Java 8-streams-collectors-patterns

Les Streams sont parmi nous
Les Streams sont parmi nousLes Streams sont parmi nous
Les Streams sont parmi nous
José Paumard
 
Monitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et PinbaMonitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et Pinba
Patrick Allaert
 
Les concepts de la programmation fonctionnelle illustrés avec java 8
Les concepts de la programmation fonctionnelle illustrés avec java 8Les concepts de la programmation fonctionnelle illustrés avec java 8
Les concepts de la programmation fonctionnelle illustrés avec java 8
Yannick Chartois
 
Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...
Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...
Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...
ATPENSC-Group
 
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Normandy JUG
 
Big Data Viz (and much more!) with Apache Zeppelin
Big Data Viz (and much more!) with Apache ZeppelinBig Data Viz (and much more!) with Apache Zeppelin
Big Data Viz (and much more!) with Apache Zeppelin
Bruno Bonnin
 
Geek Time Janvier 2017 : Java 8
Geek Time Janvier 2017 : Java 8Geek Time Janvier 2017 : Java 8
Geek Time Janvier 2017 : Java 8
OLBATI
 
cours1.ppt
cours1.pptcours1.ppt
cours1.ppt
testuser715939
 
Génération automatique de texte
Génération automatique de texteGénération automatique de texte
Génération automatique de texte
Estelle Delpech
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation Groovy
guest6e3bed
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation Groovy
JS Bournival
 
Rich Desktop Applications
Rich Desktop ApplicationsRich Desktop Applications
Rich Desktop Applications
goldoraf
 
Réu technodejs
Réu technodejsRéu technodejs
Réu technodejs
naholyr
 
Les concepts de la programmation fonctionnelle illustrés avec Java 8
Les concepts de la programmation fonctionnelle illustrés avec Java 8Les concepts de la programmation fonctionnelle illustrés avec Java 8
Les concepts de la programmation fonctionnelle illustrés avec Java 8
Yannick Chartois
 
Java 8 : Un ch'ti peu de lambda
Java 8 : Un ch'ti peu de lambdaJava 8 : Un ch'ti peu de lambda
Java 8 : Un ch'ti peu de lambda
Ch'ti JUG
 
201303 - Java8
201303 - Java8201303 - Java8
201303 - Java8
lyonjug
 

Similaire à Java 8-streams-collectors-patterns (20)

Les Streams sont parmi nous
Les Streams sont parmi nousLes Streams sont parmi nous
Les Streams sont parmi nous
 
Monitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et PinbaMonitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et Pinba
 
Les concepts de la programmation fonctionnelle illustrés avec java 8
Les concepts de la programmation fonctionnelle illustrés avec java 8Les concepts de la programmation fonctionnelle illustrés avec java 8
Les concepts de la programmation fonctionnelle illustrés avec java 8
 
Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...
Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...
Fiche de TD 1 de préparation probatoire (littéraire et scientifique) du Camer...
 
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
 
Drools
DroolsDrools
Drools
 
Big Data Viz (and much more!) with Apache Zeppelin
Big Data Viz (and much more!) with Apache ZeppelinBig Data Viz (and much more!) with Apache Zeppelin
Big Data Viz (and much more!) with Apache Zeppelin
 
Geek Time Janvier 2017 : Java 8
Geek Time Janvier 2017 : Java 8Geek Time Janvier 2017 : Java 8
Geek Time Janvier 2017 : Java 8
 
Sds Programme
Sds ProgrammeSds Programme
Sds Programme
 
cours1.ppt
cours1.pptcours1.ppt
cours1.ppt
 
Génération automatique de texte
Génération automatique de texteGénération automatique de texte
Génération automatique de texte
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation Groovy
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation Groovy
 
Rich Desktop Applications
Rich Desktop ApplicationsRich Desktop Applications
Rich Desktop Applications
 
Réu technodejs
Réu technodejsRéu technodejs
Réu technodejs
 
Presentation JPA
Presentation JPAPresentation JPA
Presentation JPA
 
Les concepts de la programmation fonctionnelle illustrés avec Java 8
Les concepts de la programmation fonctionnelle illustrés avec Java 8Les concepts de la programmation fonctionnelle illustrés avec Java 8
Les concepts de la programmation fonctionnelle illustrés avec Java 8
 
Java 8 : Un ch'ti peu de lambda
Java 8 : Un ch'ti peu de lambdaJava 8 : Un ch'ti peu de lambda
Java 8 : Un ch'ti peu de lambda
 
201303 - Java8
201303 - Java8201303 - Java8
201303 - Java8
 
Php seance1
Php seance1Php seance1
Php seance1
 

Plus de José Paumard

Loom Virtual Threads in the JDK 19
Loom Virtual Threads in the JDK 19Loom Virtual Threads in the JDK 19
Loom Virtual Threads in the JDK 19
José Paumard
 
From Java 11 to 17 and beyond.pdf
From Java 11 to 17 and beyond.pdfFrom Java 11 to 17 and beyond.pdf
From Java 11 to 17 and beyond.pdf
José Paumard
 
The Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern MatchingThe Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern Matching
José Paumard
 
Deep Dive Java 17 Devoxx UK
Deep Dive Java 17 Devoxx UKDeep Dive Java 17 Devoxx UK
Deep Dive Java 17 Devoxx UK
José Paumard
 
Designing functional and fluent API: application to some GoF patterns
Designing functional and fluent API: application to some GoF patternsDesigning functional and fluent API: application to some GoF patterns
Designing functional and fluent API: application to some GoF patterns
José Paumard
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of Flattery
José Paumard
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of Flattery
José Paumard
 
Designing functional and fluent API: example of the Visitor Pattern
Designing functional and fluent API: example of the Visitor PatternDesigning functional and fluent API: example of the Visitor Pattern
Designing functional and fluent API: example of the Visitor Pattern
José Paumard
 
Construire son JDK en 10 étapes
Construire son JDK en 10 étapesConstruire son JDK en 10 étapes
Construire son JDK en 10 étapes
José Paumard
 
Java Keeps Throttling Up!
Java Keeps Throttling Up!Java Keeps Throttling Up!
Java Keeps Throttling Up!
José Paumard
 
Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2
José Paumard
 
Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1
José Paumard
 
Asynchronous Systems with Fn Flow
Asynchronous Systems with Fn FlowAsynchronous Systems with Fn Flow
Asynchronous Systems with Fn Flow
José Paumard
 
Java Full Throttle
Java Full ThrottleJava Full Throttle
Java Full Throttle
José Paumard
 
JAX-RS and CDI Bike the (Reactive) Bridge
JAX-RS and CDI Bike the (Reactive) BridgeJAX-RS and CDI Bike the (Reactive) Bridge
JAX-RS and CDI Bike the (Reactive) Bridge
José Paumard
 
Collectors in the Wild
Collectors in the WildCollectors in the Wild
Collectors in the Wild
José Paumard
 
Streams in the wild
Streams in the wildStreams in the wild
Streams in the wild
José Paumard
 
JAX RS and CDI bike the reactive bridge
JAX RS and CDI bike the reactive bridgeJAX RS and CDI bike the reactive bridge
JAX RS and CDI bike the reactive bridge
José Paumard
 
Free your lambdas
Free your lambdasFree your lambdas
Free your lambdas
José Paumard
 
Linked to ArrayList: the full story
Linked to ArrayList: the full storyLinked to ArrayList: the full story
Linked to ArrayList: the full story
José Paumard
 

Plus de José Paumard (20)

Loom Virtual Threads in the JDK 19
Loom Virtual Threads in the JDK 19Loom Virtual Threads in the JDK 19
Loom Virtual Threads in the JDK 19
 
From Java 11 to 17 and beyond.pdf
From Java 11 to 17 and beyond.pdfFrom Java 11 to 17 and beyond.pdf
From Java 11 to 17 and beyond.pdf
 
The Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern MatchingThe Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern Matching
 
Deep Dive Java 17 Devoxx UK
Deep Dive Java 17 Devoxx UKDeep Dive Java 17 Devoxx UK
Deep Dive Java 17 Devoxx UK
 
Designing functional and fluent API: application to some GoF patterns
Designing functional and fluent API: application to some GoF patternsDesigning functional and fluent API: application to some GoF patterns
Designing functional and fluent API: application to some GoF patterns
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of Flattery
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of Flattery
 
Designing functional and fluent API: example of the Visitor Pattern
Designing functional and fluent API: example of the Visitor PatternDesigning functional and fluent API: example of the Visitor Pattern
Designing functional and fluent API: example of the Visitor Pattern
 
Construire son JDK en 10 étapes
Construire son JDK en 10 étapesConstruire son JDK en 10 étapes
Construire son JDK en 10 étapes
 
Java Keeps Throttling Up!
Java Keeps Throttling Up!Java Keeps Throttling Up!
Java Keeps Throttling Up!
 
Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2
 
Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1
 
Asynchronous Systems with Fn Flow
Asynchronous Systems with Fn FlowAsynchronous Systems with Fn Flow
Asynchronous Systems with Fn Flow
 
Java Full Throttle
Java Full ThrottleJava Full Throttle
Java Full Throttle
 
JAX-RS and CDI Bike the (Reactive) Bridge
JAX-RS and CDI Bike the (Reactive) BridgeJAX-RS and CDI Bike the (Reactive) Bridge
JAX-RS and CDI Bike the (Reactive) Bridge
 
Collectors in the Wild
Collectors in the WildCollectors in the Wild
Collectors in the Wild
 
Streams in the wild
Streams in the wildStreams in the wild
Streams in the wild
 
JAX RS and CDI bike the reactive bridge
JAX RS and CDI bike the reactive bridgeJAX RS and CDI bike the reactive bridge
JAX RS and CDI bike the reactive bridge
 
Free your lambdas
Free your lambdasFree your lambdas
Free your lambdas
 
Linked to ArrayList: the full story
Linked to ArrayList: the full storyLinked to ArrayList: the full story
Linked to ArrayList: the full story
 

Dernier

MS-203 Microsoft 365 Messaging Study Guide to prepare the certification
MS-203 Microsoft 365 Messaging Study Guide to prepare the certificationMS-203 Microsoft 365 Messaging Study Guide to prepare the certification
MS-203 Microsoft 365 Messaging Study Guide to prepare the certification
OlivierLumeau1
 
Présentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Présentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPrésentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Présentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
siemaillard
 
L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...
L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...
L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...
Faga1939
 
[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf
[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf
[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf
mcevapi3
 
1eT Revolutions Empire Revolution Empire
1eT Revolutions Empire Revolution Empire1eT Revolutions Empire Revolution Empire
1eT Revolutions Empire Revolution Empire
NadineHG
 
Cours Gestion d’actifs BNP -- CAMGESTION
Cours Gestion d’actifs BNP -- CAMGESTIONCours Gestion d’actifs BNP -- CAMGESTION
Cours Gestion d’actifs BNP -- CAMGESTION
Sékou Oumar SYLLA
 
A2-Critiques-gastronomiques activités critiques
A2-Critiques-gastronomiques activités critiquesA2-Critiques-gastronomiques activités critiques
A2-Critiques-gastronomiques activités critiques
lebaobabbleu
 
Zineb Mekouar.pptx Écrivaine marocaine
Zineb Mekouar.pptx   Écrivaine  marocaineZineb Mekouar.pptx   Écrivaine  marocaine
Zineb Mekouar.pptx Écrivaine marocaine
Txaruka
 
A2-Faire-une-appreciation positive et/ou négative (A2)
A2-Faire-une-appreciation positive et/ou négative (A2)A2-Faire-une-appreciation positive et/ou négative (A2)
A2-Faire-une-appreciation positive et/ou négative (A2)
lebaobabbleu
 
Microbiologie: le monde microbien et les techniques de mise en évidence.
Microbiologie: le monde microbien et les techniques de mise en évidence.Microbiologie: le monde microbien et les techniques de mise en évidence.
Microbiologie: le monde microbien et les techniques de mise en évidence.
MahouwetinJacquesGBO
 
Formation M2i - Attitude constructive : développer l'art de l'optimisme
Formation M2i - Attitude constructive : développer l'art de l'optimismeFormation M2i - Attitude constructive : développer l'art de l'optimisme
Formation M2i - Attitude constructive : développer l'art de l'optimisme
M2i Formation
 
Burkina Faso libraries newsletter for June 2024
Burkina Faso libraries newsletter for June 2024Burkina Faso libraries newsletter for June 2024
Burkina Faso libraries newsletter for June 2024
Friends of African Village Libraries
 
apprendre-a-programmer-avec-python-3.pdf
apprendre-a-programmer-avec-python-3.pdfapprendre-a-programmer-avec-python-3.pdf
apprendre-a-programmer-avec-python-3.pdf
kamouzou878
 
Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...
Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...
Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...
dokposeverin
 
MARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptx
MARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptxMARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptx
MARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptx
Martin M Flynn
 

Dernier (15)

MS-203 Microsoft 365 Messaging Study Guide to prepare the certification
MS-203 Microsoft 365 Messaging Study Guide to prepare the certificationMS-203 Microsoft 365 Messaging Study Guide to prepare the certification
MS-203 Microsoft 365 Messaging Study Guide to prepare the certification
 
Présentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Présentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPrésentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Présentation3.pptxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 
L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...
L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...
L'ÉDUCATION AVEC INTELLIGENCE ARTIFICIELLE ET LES DÉFICIENCES DE SON APPLICAT...
 
[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf
[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf
[218_phot_d'Autriche-Hongrie_et_des_[...]Vaffier_Hubert_btv1b8594559c.pdf
 
1eT Revolutions Empire Revolution Empire
1eT Revolutions Empire Revolution Empire1eT Revolutions Empire Revolution Empire
1eT Revolutions Empire Revolution Empire
 
Cours Gestion d’actifs BNP -- CAMGESTION
Cours Gestion d’actifs BNP -- CAMGESTIONCours Gestion d’actifs BNP -- CAMGESTION
Cours Gestion d’actifs BNP -- CAMGESTION
 
A2-Critiques-gastronomiques activités critiques
A2-Critiques-gastronomiques activités critiquesA2-Critiques-gastronomiques activités critiques
A2-Critiques-gastronomiques activités critiques
 
Zineb Mekouar.pptx Écrivaine marocaine
Zineb Mekouar.pptx   Écrivaine  marocaineZineb Mekouar.pptx   Écrivaine  marocaine
Zineb Mekouar.pptx Écrivaine marocaine
 
A2-Faire-une-appreciation positive et/ou négative (A2)
A2-Faire-une-appreciation positive et/ou négative (A2)A2-Faire-une-appreciation positive et/ou négative (A2)
A2-Faire-une-appreciation positive et/ou négative (A2)
 
Microbiologie: le monde microbien et les techniques de mise en évidence.
Microbiologie: le monde microbien et les techniques de mise en évidence.Microbiologie: le monde microbien et les techniques de mise en évidence.
Microbiologie: le monde microbien et les techniques de mise en évidence.
 
Formation M2i - Attitude constructive : développer l'art de l'optimisme
Formation M2i - Attitude constructive : développer l'art de l'optimismeFormation M2i - Attitude constructive : développer l'art de l'optimisme
Formation M2i - Attitude constructive : développer l'art de l'optimisme
 
Burkina Faso libraries newsletter for June 2024
Burkina Faso libraries newsletter for June 2024Burkina Faso libraries newsletter for June 2024
Burkina Faso libraries newsletter for June 2024
 
apprendre-a-programmer-avec-python-3.pdf
apprendre-a-programmer-avec-python-3.pdfapprendre-a-programmer-avec-python-3.pdf
apprendre-a-programmer-avec-python-3.pdf
 
Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...
Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...
Manuel-5.-Elevage-de-poisson-chat-africain-Clarias-gariepinus-en-bacs-hors-so...
 
MARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptx
MARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptxMARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptx
MARTYRS DE HOLLANDE - La révolte hollandaise et les guerres de religion..pptx
 

Java 8-streams-collectors-patterns

  • 1. Java 8 Streams & Collectors Patterns, performance, parallélisation @JosePaumard
  • 9. @JosePaumard#Stream8 Du code et des slides 1ère partie : des slides - Stream - Opérations - État - Réduction - Collector 2ème partie : code + slides - Houston - Shakespeare - Actors
  • 13. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Techniquement : une interface paramétrée public interface Stream<T> extends BaseStream<T, Stream<T>> { // ... }
  • 14. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Techniquement : une interface paramétrée Pratiquement : un nouveau concept public interface Stream<T> extends BaseStream<T, Stream<T>> { // ... }
  • 15. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? À quoi un Stream sert-il ?
  • 16. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? À quoi un Stream sert-il ? Réponse : à traiter efficacement les grands volumes de données, et aussi les petits
  • 17. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Que signifie efficacement ?
  • 18. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Que signifie efficacement ? Deux choses :
  • 19. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Que signifie efficacement ? Deux choses : 1) en parallèle, pour exploiter le multicœur
  • 20. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Que signifie efficacement ? Deux choses : 1) en parallèle, pour exploiter le multicœur 2) en pipeline, pour éviter les intermédiaires de calcul
  • 21. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Pourquoi une collection ne peut-elle constituer un Stream ?
  • 22. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Pourquoi une collection ne peut-elle constituer un Stream ? Il y a des arguments pour qu’une collection soit un Stream !
  • 23. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Pourquoi une collection ne peut-elle constituer un Stream ? Il y a des arguments pour qu’une collection soit un Stream ! 1) mes données sont dans des collections (ou tables de hachage)
  • 24. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Pourquoi une collection ne peut-elle constituer un Stream ? Il y a des arguments pour qu’une collection soit un Stream ! 1) mes données sont dans des collections (ou tables de hachage) 2) on connaît bien l’API Collection…
  • 25. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Pourquoi une collection ne peut-elle constituer un Stream ? Deux raisons : 1) ça permet d’avoir les mains libres
  • 26. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Pourquoi une collection ne peut-elle constituer un Stream ? Deux raisons : 1) ça permet d’avoir les mains libres 2) ça permet de ne pas polluer l’API Collection avec ces nouveaux concepts
  • 27. @JosePaumard#Stream8 Donc : qu’est-ce qu’un Stream ? Réponses :
  • 28. @JosePaumard#Stream8 Donc : qu’est-ce qu’un Stream ? Réponses : 1) un objet qui sert à définir des opérations
  • 29. @JosePaumard#Stream8 Donc : qu’est-ce qu’un Stream ? Réponses : 1) un objet qui sert à définir des opérations 2) qui ne possède pas les données qu’il traite (source)
  • 30. @JosePaumard#Stream8 Donc : qu’est-ce qu’un Stream ? Réponses : 1) un objet qui sert à définir des opérations 2) qui ne possède pas les données qu’il traite (source) 3) qui s’interdit de modifier les données qu’il traite
  • 31. @JosePaumard#Stream8 Donc : qu’est-ce qu’un Stream ? Réponses : 1) un objet qui sert à définir des opérations 2) qui ne possède pas les données qu’il traite (source) 3) qui s’interdit de modifier les données qu’il traite 4) qui traite les données en « une passe »
  • 32. @JosePaumard#Stream8 Donc : qu’est-ce qu’un Stream ? Réponses : 1) un objet qui sert à définir des opérations 2) qui ne possède pas les données qu’il traite (source) 3) qui s’interdit de modifier les données qu’il traite 4) qui traite les données en « une passe » 5) qui est optimisé du point de vue algorithmique et qui est capable de calculer en parallèle
  • 33. @JosePaumard#Stream8 Comment construit-on un Stream ? Plein de patterns !
  • 34. @JosePaumard#Stream8 Comment construit-on un Stream ? Plein de patterns ! Choisissons-en un : List<Person> persons = ... ; Stream<Person> stream = persons.stream() ;
  • 35. @JosePaumard#Stream8 Opérations sur un Stream Première opération : forEach() … affiche chaque personne sur la console List<Person> persons = ... ; Stream<Person> stream = persons.stream() ; stream.forEach(p -> System.out.println(p)) ;
  • 36. @JosePaumard#Stream8 Opérations sur un Stream Première opération : forEach() … affiche chaque personne sur la console List<Person> persons = ... ; Stream<Person> stream = persons.stream() ; stream.forEach(System.out::println) ;
  • 37. @JosePaumard#Stream8 Opérations sur un Stream Première opération : forEach() Method reference : List<Person> persons = ... ; Stream<Person> stream = persons.stream() ; stream.forEach(System.out::println) ; o -> System.out.println(o) ≡ System.out::println
  • 38. @JosePaumard#Stream8 Opération forEach() Première opération : forEach() forEach() : prend un Consumer<T> en paramètre @FunctionalInterface public interface Consumer<T> { void accept(T t) ; }
  • 39. @JosePaumard#Stream8 Opération forEach() Première opération : forEach() forEach() : prend un Consumer<T> en paramètre Interface fonctionnelle ? @FunctionalInterface public interface Consumer<T> { void accept(T t) ; }
  • 40. @JosePaumard#Stream8 Sauf qu’en fait… Consumer est un peu plus complexe que ça : @FunctionalInterface public interface Consumer<T> { void accept(T t) ; default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
  • 41. @JosePaumard#Stream8 Sauf qu’en fait… Consumer est un peu plus complexe que ça : Méthode par défaut ? @FunctionalInterface public interface Consumer<T> { void accept(T t) ; default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
  • 42. @JosePaumard#Stream8 Interface fonctionnelle = une interface qui ne possède qu’une seule méthode
  • 43. @JosePaumard#Stream8 Interface fonctionnelle = une interface qui ne possède qu’une seule méthode « peut » être annotée par @FunctionalInterface
  • 44. @JosePaumard#Stream8 Interface fonctionnelle = une interface qui ne possède qu’une seule méthode « peut » être annotée par @FunctionalInterface Les méthodes de Object ne « comptent » pas
  • 45. @JosePaumard#Stream8 Interface fonctionnelle = une interface qui ne possède qu’une seule méthode « peut » être annotée par @FunctionalInterface Les méthodes de Object ne « comptent » pas Peut comporter des méthodes « par défaut »
  • 47. @JosePaumard#Stream8 Méthodes par défaut Nouveauté Java 8 ! Intégrée pour permettre de faire évoluer des vieilles interfaces
  • 48. @JosePaumard#Stream8 Méthodes par défaut Nouveauté Java 8 ! Intégrée pour permettre de faire évoluer des vieilles interfaces Cas de Collection : on a ajouté stream()
  • 49. @JosePaumard#Stream8 Méthodes par défaut Quid de l’héritage multiple ?
  • 50. @JosePaumard#Stream8 Méthodes par défaut Quid de l’héritage multiple ? On a déjà l’héritage multiple en Java ! public final class String implements Serializable, Comparable<String>, CharSequence { // ... }
  • 51. @JosePaumard#Stream8 Méthodes par défaut Quid de l’héritage multiple ? On a déjà l’héritage multiple en Java ! On a l’héritage multiple de type public final class String implements Serializable, Comparable<String>, CharSequence { // ... }
  • 52. @JosePaumard#Stream8 Méthodes par défaut Java 8 amène l’héritage multiple d’implémentation
  • 53. @JosePaumard#Stream8 Méthodes par défaut Java 8 amène l’héritage multiple d’implémentation Ce que l’on n’a pas, c’est l’héritage multiple d’état
  • 54. @JosePaumard#Stream8 Méthodes par défaut Java 8 amène l’héritage multiple d’implémentation Ce que l’on n’a pas, c’est l’héritage multiple d’état et d’ailleurs… on n’en veut pas !
  • 57. @JosePaumard#Stream8 Méthodes par défaut public class A implements B, C { } public interface C { default String a() {...} } public interface B { default String a() {...} }
  • 58. @JosePaumard#Stream8 Méthodes par défaut Conflit : erreur de compilation ! public class A implements B, C { } public interface C { default String a() {...} } public interface B { default String a() {...} }
  • 59. @JosePaumard#Stream8 Méthodes par défaut Pour lever l’erreur : deux solutions public class A implements B, C { } public interface C { default String a() {...} } public interface B { default String a() {...} }
  • 60. @JosePaumard#Stream8 Méthodes par défaut 1) La classe gagne ! public class A implements B, C { public String a() {...} } public interface C { default String a() {...} } public interface B { default String a() {...} }
  • 61. @JosePaumard#Stream8 Méthodes par défaut 1) La classe gagne ! public class A implements B, C { public String a() { B.super.a() ; } } public interface C { default String a() {...} } public interface B { default String a() {...} }
  • 62. @JosePaumard#Stream8 Méthodes par défaut 2) Le plus spécifique gagne ! public class A implements B, C { } public interface B extends C { default String a() {...} } public interface C { default String a() {...} }
  • 63. @JosePaumard#Stream8 Méthodes par défaut Conflits ? oui… 2 règles pour les gérer :
  • 64. @JosePaumard#Stream8 Méthodes par défaut Conflits ? oui… 2 règles pour les gérer : 1) La classe gagne !
  • 65. @JosePaumard#Stream8 Méthodes par défaut Conflits ? oui… 2 règles pour les gérer : 1) La classe gagne ! 2) Le plus spécifique gagne !
  • 66. @JosePaumard#Stream8 Retour sur Consumer @FunctionalInterface public interface Consumer<T> { void accept(T t) ; default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
  • 67. @JosePaumard#Stream8 Retour sur Consumer List<String> liste = new ArrayList<>() ; Consumer<String> c1 = s -> liste.add(s) ; Consumer<String> c2 = s -> System.out.println(s) ;
  • 68. @JosePaumard#Stream8 Retour sur Consumer List<String> liste = new ArrayList<>() ; Consumer<String> c1 = liste::add ; Consumer<String> c2 = System.out::println ;
  • 69. @JosePaumard#Stream8 Retour sur Consumer List<String> liste = new ArrayList<>() ; Consumer<String> c1 = liste::add ; Consumer<String> c2 = System.out::println ; Consumer<String> c3 = c1.andThen(c2) ; // et on pourrait continuer
  • 70. @JosePaumard#Stream8 Retour sur Consumer Attention à la concurrence ! List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; Consumer<String> c1 = liste::add ; persons.stream() .forEach(c1) ; // concurrence ?
  • 71. @JosePaumard#Stream8 Retour sur Consumer Attention à la concurrence ! List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; Consumer<String> c1 = liste::add ; persons.stream().parallel() .forEach(c1) ; // concurrence ?
  • 72. @JosePaumard#Stream8 Retour sur Consumer Attention à la concurrence ! List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; Consumer<String> c1 = liste::add ; persons.stream() .forEach(c1) ; // concurrence ? Baaad pattern !
  • 73. @JosePaumard#Stream8 Retour sur Consumer Problème : forEach() ne retourne rien List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; Consumer<String> c1 = liste::add ; Consumer<String> c2 = System.out::println persons.stream() .forEach(c1.andThen(c2)) ;
  • 74. @JosePaumard#Stream8 Retour sur Consumer Peek à la rescousse ! List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; persons.stream() .peek(System.out::println) .filter(person -> person.getAge() > 20) .peek(resultat::add) ; // Baaad pattern !
  • 75. @JosePaumard#Stream8 Retour sur Stream Donc on a : - une méthode forEach(Consumer) - une méthode peek(Consumer)
  • 76. @JosePaumard#Stream8 Retour sur Stream 3ème méthode : filter(Predicate)
  • 77. @JosePaumard#Stream8 Méthode filter() 3ème méthode : filter(Predicate) List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<Person> filtered = stream.filter(person -> person.getAge() > 20) ;
  • 78. @JosePaumard#Stream8 Méthode filter() 3ème méthode : filter(Predicate) List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<Person> filtered = stream.filter(person -> person.getAge() > 20) ; Predicate<Person> p = person -> person.getAge() > 20 ;
  • 80. @JosePaumard#Stream8 Interface Predicate En fait … @FunctionalInterface public interface Predicate<T> { boolean test(T t) ; default Predicate<T> and(Predicate<? super T> other) { ... } default Predicate<T> or(Predicate<? super T> other) { ... } default Predicate<T> negate() { ... } }
  • 81. @JosePaumard#Stream8 Interface Predicate default Predicate<T> and(Predicate<? super T> other) { return t -> test(t) && other.test(t) ; } default Predicate<T> or(Predicate<? super T> other) { return t -> test(t) || other.test(t) ; } default Predicate<T> negate() { return t -> !test(t) ; }
  • 82. @JosePaumard#Stream8 Interface Predicate : patterns Predicate<Integer> p1 = i -> i > 20 ; Predicate<Integer> p2 = i -> i < 30 ; Predicate<Integer> p3 = i -> i == 0 ;
  • 83. @JosePaumard#Stream8 Interface Predicate : patterns Predicate<Integer> p1 = i -> i > 20 ; Predicate<Integer> p2 = i -> i < 30 ; Predicate<Integer> p3 = i -> i == 0 ; Predicate<Integer> p = p1.and(p2).or(p3) ; // (p1 AND p2) OR p3
  • 84. @JosePaumard#Stream8 Interface Predicate : patterns Predicate<Integer> p1 = i -> i > 20 ; Predicate<Integer> p2 = i -> i < 30 ; Predicate<Integer> p3 = i -> i == 0 ; Predicate<Integer> p = p1.and(p2).or(p3) ; // (p1 AND p2) OR p3 Predicate<Integer> p = p3.or(p1).and(p2) ; // (p3 OR p1) AND p2
  • 85. @JosePaumard#Stream8 Interface Predicate En fait (bis) … @FunctionalInterface public interface Predicate<T> { boolean test(T t) ; // méthodes par défaut static <T> Predicate<T> isEqual(Object o) { ... } }
  • 86. @JosePaumard#Stream8 Interface Predicate static <T> Predicate<T> isEqual(Object o) { return t -> o.equals(t) ; }
  • 87. @JosePaumard#Stream8 Interface Predicate En fait : static <T> Predicate<T> isEqual(Object o) { return t -> o.equals(t) ; } static <T> Predicate<T> isEqual(Object o) { return (null == o) ? obj -> Objects.isNull(obj) : t -> o.equals(t) ; }
  • 88. @JosePaumard#Stream8 Interface Predicate En fait : static <T> Predicate<T> isEqual(Object o) { return t -> o.equals(t) ; } static <T> Predicate<T> isEqual(Object o) { return (null == o) ? Objects::isNull : o::equals ; }
  • 89. @JosePaumard#Stream8 Interface Predicate : patterns Predicate<String> p = Predicate.isEqual("deux") ;
  • 90. @JosePaumard#Stream8 Interface Predicate : patterns Predicate<String> p = Predicate.isEqual("deux") ; Stream<String> stream1 = Stream.of("un", "deux", "trois") ; Stream<String> stream2 = stream1.filter(p) ;
  • 91. @JosePaumard#Stream8 Interface Predicate : remarque Dans ce code : Les deux streams stream et filtered sont différents La méthode filter() retourne un nouvel objet List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<Person> filtered = stream.filter(person -> person.getAge() > 20) ;
  • 92. @JosePaumard#Stream8 Interface Predicate : remarque Question : qu’est-ce que j’ai dans cet objet ?
  • 93. @JosePaumard#Stream8 Interface Predicate : remarque Question : qu’est-ce que j’ai dans cet objet ? Réponse : mes données filtrées
  • 94. @JosePaumard#Stream8 Interface Predicate : remarque Question : qu’est-ce que j’ai dans cet objet ? Réponse : mes données filtrées vraiment ?
  • 95. @JosePaumard#Stream8 Interface Predicate : remarque Question : qu’est-ce que j’ai dans cet objet ? Réponse : mes données filtrées vraiment ? « un stream ne porte pas de données »…
  • 96. @JosePaumard#Stream8 Interface Predicate : remarque Question : qu’est-ce que j’ai dans cet objet ? Réponse : mes données filtrées
  • 97. @JosePaumard#Stream8 Interface Predicate : remarque Question : qu’est-ce que j’ai dans cet objet ? Réponse : mes données filtrées Réponse : rien !
  • 98. @JosePaumard#Stream8 Interface Predicate : remarque Question 2 : que se passe-t-il lors de l’exécution de ce code ? List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<Person> filtered = stream.filter(person -> person.getAge() > 20) ;
  • 99. @JosePaumard#Stream8 Interface Predicate : remarque Question 2 : que se passe-t-il lors de l’exécution de ce code ? Réponse : rien ! List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<Person> filtered = stream.filter(person -> person.getAge() > 20) ;
  • 100. @JosePaumard#Stream8 Interface Predicate : remarque Question 2 : que se passe-t-il lors de l’exécution de ce code ? Réponse : rien ! Cet appel est une déclaration : aucun traitement n’est fait ! List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<Person> filtered = stream.filter(person -> person.getAge() > 20) ;
  • 101. @JosePaumard#Stream8 Interface Predicate : remarque L’appel à filter() est un appel lazy D’une façon générale : un appel qui retourne un Stream est lazy
  • 102. @JosePaumard#Stream8 Interface Predicate : remarque L’appel à filter() est un appel lazy Autre façon de le dire : une opération qui retourne un Stream est une opération intermédiaire
  • 103. @JosePaumard#Stream8 Appels intermédiaires Le fait de choisir qu’un stream ne porte pas ses données crée la notion d’opération intermédiaire
  • 104. @JosePaumard#Stream8 Appels intermédiaires Le fait de choisir qu’un stream ne porte pas ses données crée la notion d’opération intermédiaire On doit bien avoir des opérations terminales quelque part…
  • 105. @JosePaumard#Stream8 Retour sur Consumer (bis) Que se passe-t-il dans ce code ? List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; persons.stream() .peek(System.out::println) .filter(person -> person.getAge() > 20) .peek(resultat::add) ; // Baaad pattern !
  • 106. @JosePaumard#Stream8 Retour sur Consumer (bis) Que se passe-t-il dans ce code ? Rien ! List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; persons.stream() .peek(System.out::println) .filter(person -> person.getAge() > 20) .peek(resultat::add) ; // Baaad pattern !
  • 107. @JosePaumard#Stream8 Retour sur Consumer (bis) Que se passe-t-il dans ce code ? 1) rien ne s’affiche ! 2) resultat reste vide ! List<String> resultat = new ArrayList<>() ; List<Person> persons = ... ; persons.stream() .peek(System.out::println) .filter(person -> person.getAge() > 20) .peek(resultat::add) ; // Baaad pattern !
  • 108. @JosePaumard#Stream8 Retour sur Stream Donc on a : - une méthode forEach(Consumer) - une méthode peek(Consumer) - une méthode filter(Predicate)
  • 110. @JosePaumard#Stream8 Continuons Opération de mapping List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<String> names = stream.map(person -> person.getNom()) ;
  • 111. @JosePaumard#Stream8 Continuons Opération de mapping Retourne un stream (différent du stream original) Donc opération intermédiaire List<Person> liste = ... ; Stream<Person> stream = liste.stream() ; Stream<String> names = stream.map(person -> person.getNom()) ;
  • 112. @JosePaumard#Stream8 Mapper : interface Function Interface fonctionnelle @FunctionalInterface public interface Function<T, R> { R apply(T t) ; }
  • 113. @JosePaumard#Stream8 Mapper : interface Function En fait @FunctionalInterface public interface Function<T, R> { R apply(T t) ; default <V> Function<V, R> compose(Function<V, T> before) ; default <V> Function<T, V> andThen(Function<R, V> after) ; }
  • 114. @JosePaumard#Stream8 Mapper : interface Function En fait Gare aux génériques ! @FunctionalInterface public interface Function<T, R> { R apply(T t) ; default <V> Function<V, R> compose(Function<V, T> before) ; default <V> Function<T, V> andThen(Function<R, V> after) ; }
  • 115. @JosePaumard#Stream8 Mapper : interface Function En fait @FunctionalInterface public interface Function<T, R> { R apply(T t) ; default <V> Function<V, R> compose( Function<? super V, ? extends T> before) ; default <V> Function<T, V> andThen( Function<? super R, ? extends V> after) ; }
  • 116. @JosePaumard#Stream8 Mapper : interface Function En fait @FunctionalInterface public interface Function<T, R> { R apply(T t) ; // méthodes par défaut static <T> Function<T, T> identity() { return t -> t ; } }
  • 117. @JosePaumard#Stream8 Retour sur Stream Donc on a : - une méthode forEach(Consumer) - une méthode peek(Consumer) - une méthode filter(Predicate) - une méthode map(Function)
  • 119. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : <R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ;
  • 120. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : <R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ; <R> Stream<R> map(Function<T, R> mapper) ;
  • 121. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : La fonction mapper transforme des éléments T en éléments Stream<R> ... flatMap(Function<T, Stream<R>> mapper) ;
  • 122. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : S’il s’agissait d’un mapping classique flatMap() retournerait Stream<Stream<R>> ... flatMap(Function<T, Stream<R>> mapper) ;
  • 123. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : S’il s’agissait d’un mapping classique flatMap() retournerait Stream<Stream<R>> Donc un « stream de streams » ... flatMap(Function<T, Stream<R>> mapper) ;
  • 124. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : Mais il s’agit d’un flatMap ! ... flatMap(Function<T, Stream<R>> mapper) ;
  • 125. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : Mais il s’agit d’un flatMap ! Et le flatMap met le Stream<Stream> « à plat » ... flatMap(Function<T, Stream<R>> mapper) ;
  • 126. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : Mais il s’agit d’un flatMap ! Et le flatMap met le Stream<Stream> « à plat » <R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ;
  • 127. @JosePaumard#Stream8 Méthode flatMap() flatMap() = mettre à plat Signature : Retourne un stream, donc opération intermédiaire <R> Stream<R> flatMap(Function<T, Stream<R>> mapper) ;
  • 128. @JosePaumard#Stream8 Bilan sur Stream On a 3 types de méthodes : - forEach() : consomme - peek() : regarde et passe à un consommateur - filter() : filtre - map() : mappe = transforme - flatMap() : mappe et met à plat
  • 129. @JosePaumard#Stream8 Bilan sur Stream On a 3 types de méthodes : - forEach() : consomme - peek() : regarde et passe à un consommateur - filter() : filtre - map() : mappe = transforme - flatMap() : mappe et met à plat Réduction ?
  • 130. @JosePaumard#Stream8 Réduction Méthode de réduction : List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; Integer somme = stream.reduce(0, (age1, age2) -> age1 + age2) ;
  • 131. @JosePaumard#Stream8 Réduction Méthode de réduction : Expression lambda sur deux éléments : applicable à tout un stream List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; Integer somme = stream.reduce(0, (age1, age2) -> age1 + age2) ;
  • 142. @JosePaumard#Stream8 Réduction Doit être associative… Reducer r1 = (i1, i2) -> i1 + i2 ; // ok Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // oooops...
  • 143. @JosePaumard#Stream8 Réduction Doit être associative… Reducer r1 = (i1, i2) -> i1 + i2 ; // ok Reducer r2 = (i1, i2) -> i1*i1 + i2*i2 ; // oooops... Reducer r3 = (i1, i2) -> (i1 + i2)/2 ; // re-oooops...
  • 144. @JosePaumard#Stream8 Réduction Méthode de réduction : Expression lambda sur deux éléments : applicable à tout un stream List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; Integer somme = stream.reduce(0, (age1, age2) -> age1 + age2) ;
  • 145. @JosePaumard#Stream8 Réduction Méthode de réduction : 0 : valeur par défaut pour le cas où la source est vide List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; Integer somme = stream.reduce(0, (age1, age2) -> age1 + age2) ;
  • 146. @JosePaumard#Stream8 Réduction Autre méthode de réduction : Quel type de retour pour max ? (ou min…) List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; ... max = stream.max(Comparator.naturalOrder()) ;
  • 147. @JosePaumard#Stream8 Type de retour pour la réduction Quel est la valeur par défaut du max ? 1) la valeur par défaut est la réduction de l’ensemble vide 2) mais c’est aussi l’élément neutre de la réduction
  • 158. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est l’élément neutre pour le max ? 0 ?
  • 159. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est l’élément neutre pour le max ? 0 ? max(0, -1) n’est pas égal à 0
  • 160. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est l’élément neutre pour le max ? 0 ? max(0, -1) n’est pas égal à 0 -∞ ?
  • 161. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est l’élément neutre pour le max ? 0 ? max(0, -1) n’est pas égal à 0 -∞ ? souci : ce n’est pas un entier
  • 162. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est l’élément neutre pour le max ? 0 ? max(0, -1) n’est pas égal à 0 -∞ ? souci : ce n’est pas un entier Integer.MIN_VALUE ?
  • 163. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est l’élément neutre pour le max ? 0 ? max(0, -1) n’est pas égal à 0 -∞ ? souci : ce n’est pas un entier Integer.MIN_VALUE ? souci pour convertir en long, et vice-versa
  • 164. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est l’élément neutre pour le max ? Réponse : il n’y a pas d’élément neutre pour le max
  • 165. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est la valeur par défaut pour le max ?
  • 166. @JosePaumard#Stream8 Type de retour pour la réduction Problème : quel est la valeur par défaut pour le max ? Réponse : il n’y a pas de valeur par défaut pour le max
  • 167. @JosePaumard#Stream8 Type de retour pour la réduction Quelle est alors le type de retour pour max() ? List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; ... max = stream.max(Comparator.naturalOrder()) ;
  • 168. @JosePaumard#Stream8 Type de retour pour la réduction Quelle est alors le type de retour pour max() ? Si on prend int, alors la valeur par défaut est 0 List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; ... max = stream.max(Comparator.naturalOrder()) ;
  • 169. @JosePaumard#Stream8 Type de retour pour la réduction Quelle est alors le type de retour pour max() ? Si on prend int, alors la valeur par défaut est 0 Si on prend Integer, alors la valeur par défaut est null List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; ... max = stream.max(Comparator.naturalOrder()) ;
  • 170. @JosePaumard#Stream8 Type de retour pour la réduction Quelle est alors le type de retour pour max() ? On a besoin d’un nouveau concept : Optional List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; Optional<Integer> max = stream.max(Comparator.naturalOrder()) ;
  • 171. @JosePaumard#Stream8 Optional Un optional encapsule un objet Peut être vide Optional<String> opt = ... ; if (opt.isPresent()) { String s = opt.get() ; } else { ... }
  • 172. @JosePaumard#Stream8 Optional Un optional encapsule un objet Peut être vide Optional<String> opt = ... ; if (opt.isPresent()) { String s = opt.get() ; } else { ... } String s = opt.orElse("") ; // défini une valeur par défaut // applicative
  • 173. @JosePaumard#Stream8 Optional Un optional encapsule un objet Peut être vide Optional<String> opt = ... ; if (opt.isPresent()) { String s = opt.get() ; } else { ... } String s = opt.orElseThrow(MyException::new) ; // construction lazy
  • 174. @JosePaumard#Stream8 Retour sur la réduction Méthodes de réduction : List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; Integer somme = stream.reduce(0, (age1, age2) -> age1 + age2) ; List<Integer> ages = ... ; Stream<Integer> stream = ages.stream() ; Optional<Integer> opt = stream.reduce((age1, age2) -> age1 + age2) ;
  • 175. @JosePaumard#Stream8 Remarque sur la réduction Une réduction ne retourne pas de Stream : - max(), min() - count() Réduction booléennes : - allMatch(), noneMatch, anyMatch() Retourne un Optional - findFirst(), findAny() (si le Stream est vide ?)
  • 176. @JosePaumard#Stream8 Remarque sur la réduction Dans tous les cas, ces réductions retournent une valeur Elles ne peuvent donc pas être évaluées de façon lazy Elles déclenchent les opérations ! Ce sont les opérations terminales
  • 177. @JosePaumard#Stream8 Opération terminale List<Person> persons = ... ; ... = persons.map(person -> person.getAge()) // retourne Stream<Integer> .filter(age -> age > 20) // retourne Stream<Integer> .min(Comparator.naturalOrder()) ;
  • 178. @JosePaumard#Stream8 Opération terminale Écriture d’un map / filter / reduce List<Person> persons = ... ; Optional<Integer> age = persons.map(person -> person.getAge()) // retourne Stream<Integer> .filter(age -> age > 20) // retourne Stream<Integer> .min(Comparator.naturalOrder()) ; // opération terminale
  • 179. @JosePaumard#Stream8 Opération terminale Écriture d’un map / filter / reduce List<Person> persons = ... ; persons.map(person -> person.getLastName()) .allMatch(length < 20) ; // opération terminale
  • 180. @JosePaumard#Stream8 Opération terminale Écriture d’un map / filter / reduce Intérêt de tout faire dans la même boucle List<Person> persons = ... ; persons.map(person -> person.getLastName()) .allMatch(length < 20) ; // opération terminale
  • 181. @JosePaumard#Stream8 Qu’est-ce qu’un Stream ? Un objet qui permet de définir des traitements sur des jeux de données arbitrairement grands Typiquement : map / filter / reduce Approche pipeline : 1) on définit des opérations 2) on lance les opérations
  • 182. @JosePaumard#Stream8 Comment un Stream est-il fait ? Que se passe-t-il lors de la construction d’un Stream ? List<Person> persons = new ArrayList<>() ; Stream<Person> stream = persons.stream() ; // interface Collection default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
  • 183. @JosePaumard#Stream8 Comment un Stream est-il fait ? Que se passe-t-il lors de la construction d’un Stream ? // classe StreamSupport public static <T> Stream<T> stream( Spliterator<T> spliterator, boolean parallel) { Objects.requireNonNull(spliterator) ; return new ReferencePipeline.Head<>( spliterator, StreamOpFlag.fromCharacteristics(spliterator), parallel) ; }
  • 184. @JosePaumard#Stream8 Comment un Stream est-il fait ? Que se passe-t-il lors de la construction d’un Stream ? // classe ArrayList @Override public Spliterator<E> spliterator() { return new ArrayListSpliterator<>(this, 0, -1, 0); }
  • 185. @JosePaumard#Stream8 Comment un Stream est-il fait ? Que se passe-t-il lors de la construction d’un Stream ? Le Spliterator encapsule la logique d’accès aux données Celui d’ArrayList manipule le tableau // classe ArrayList @Override public Spliterator<E> spliterator() { return new ArrayListSpliterator<>(this, 0, -1, 0); }
  • 186. @JosePaumard#Stream8 Fonction du Spliterator Méthodes à implémenter Consomme le prochain élément s’il existe // interface Spliterator boolean tryAdvance(Consumer<? super T> action);
  • 187. @JosePaumard#Stream8 Fonction du Spliterator Méthodes à implémenter Utilisée par le parallélisme : divise les données en deux, suivant des règles précises // interface Spliterator Spliterator<T> trySplit();
  • 188. @JosePaumard#Stream8 Fonction du Spliterator Méthodes à implémenter Retourne une estimation du nombre d’éléments de ce Stream // interface Spliterator long estimateSize();
  • 189. @JosePaumard#Stream8 Fonction du Spliterator Méthodes par défaut // interface Spliterator default void forEachRemaining(Consumer<? super T> action) { do { } while (tryAdvance(action)); } // interface Spliterator default long getExactSizeIfKnown() { return (characteristics() & SIZED) == 0 ? -1L : estimateSize(); }
  • 190. @JosePaumard#Stream8 Fonction du Spliterator Méthodes à implémenter Implémentations // interface Spliterator int characteristics(); // pour ArrayList public int characteristics() { return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; }
  • 191. @JosePaumard#Stream8 Fonction du Spliterator Méthodes à implémenter Implémentations // interface Spliterator int characteristics(); // pour HashSet public int characteristics() { return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | Spliterator.DISTINCT; }
  • 192. @JosePaumard#Stream8 Caractéristiques d’un Stream Un Stream porte des caractéristiques Caractéristique ORDERED L’ordre a un sens DISTINCT Pas de doublon SORTED Trié SIZED Le cardinal est connu NONNULL Pas de valeurs nulles IMMUTABLE Ne peut pas être modifié CONCURRENT Autorise le parallélisme SUBSIZED Le cardinal est connu
  • 193. @JosePaumard#Stream8 Caractéristiques d’un Stream Les opérations changent la valeur des caractéristiques Méthode Mets à 0 Mets à 1 Filter SIZED - Map DISTINCT, SORTED - FlatMap DISTINCT, SORTED, SIZED - Sorted - SORTED, ORDERED Distinct - DISTINCT Limit SIZED - Peek - - Unordered ORDERED -
  • 194. @JosePaumard#Stream8 Caractéristiques d’un Stream Les caractéristiques d’un Stream sont prise en compte à la consommation // HashSet HashSet<String> strings = ... ; strings.stream() .distinct() // ne déclenche pas de traitement .sorted() .collect(Collectors.toList()) ;
  • 195. @JosePaumard#Stream8 Implémentations d’un Stream Complexe Partagé en deux : 1) partie algorithmique, on n’a pas envie d’y toucher 2) partie accès aux données : faite pour être surchargée !
  • 196. @JosePaumard#Stream8 Apparté sur les Comparator On a écrit : // interface Comparator Comparator cmp = Comparator.naturalOrder() ;
  • 197. @JosePaumard#Stream8 Apparté sur les Comparator On a écrit : // interface Comparator Comparator cmp = Comparator.naturalOrder() ; // interface Comparator @SuppressWarnings("unchecked") public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() { return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE; }
  • 198. @JosePaumard#Stream8 Apparté sur les Comparator // classe Comparators enum NaturalOrderComparator implements Comparator<Comparable<Object>> { INSTANCE; public int compare(Comparable<Object> c1, Comparable<Object> c2) { return c1.compareTo(c2); } public Comparator<Comparable<Object>> reversed() { return Comparator.reverseOrder(); } }
  • 199. @JosePaumard#Stream8 Apparté sur les Comparator On peut écrire : // interface Comparator Comparator cmp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person::getAge) ;
  • 200. @JosePaumard#Stream8 Apparté sur les Comparator Méthode comparing() // interface Comparator public static <T, U> Comparator<T> comparing(Function<T, U> keyExtractor) { Objects.requireNonNull(keyExtractor); return (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); }
  • 201. @JosePaumard#Stream8 Apparté sur les Comparator Méthode comparing() // interface Comparator public static <T, U extends Comparable<U>> Comparator<T> comparing(Function<T, U> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T>) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); }
  • 202. @JosePaumard#Stream8 Apparté sur les Comparator Méthode comparing() // interface Comparator public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); }
  • 203. @JosePaumard#Stream8 Apparté sur les Comparator Méthode thenComparing() // interface Comparator default <U> Comparator<T> thenComparing(Function<T, U> keyExtractor) { return thenComparing(comparing(keyExtractor)); }
  • 204. @JosePaumard#Stream8 Apparté sur les Comparator Méthode thenComparing() // interface Comparator default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T, ? extends U> keyExtractor) { return thenComparing(comparing(keyExtractor)); }
  • 205. @JosePaumard#Stream8 Apparté sur les Comparator Méthode thenComparing() // interface Comparator default Comparator<T> thenComparing(Comparator<? super T> other) { Objects.requireNonNull(other); return (Comparator<T> & Serializable) (c1, c2) -> { int res = compare(c1, c2); return (res != 0) ? res : other.compare(c1, c2); }; }
  • 206. @JosePaumard#Stream8 Petit bilan API Stream - opérations intermédiaires - opérations terminales - implémentations à deux niveaux
  • 207. @JosePaumard#Stream8 Opérations stateless / stateful Le code suivant : … sélectionne les 1000 premières personnes ArrayList<Person> persons = ... ; Stream<Persons> stream = persons.limit(1_000) ;
  • 208. @JosePaumard#Stream8 Opérations stateless / stateful Le code suivant : … sélectionne les 1000 premières personnes Cette opération a besoin d’un compteur Parallélisme ? ArrayList<Person> persons = ... ; Stream<Persons> stream = persons.limit(1_000) ;
  • 209. @JosePaumard#Stream8 Opérations stateless / stateful Le code suivant : ArrayList<Person> persons = ... ; List<String> names = persons.map(Person::getLastName) .collect(Collectors.toList()) ;
  • 210. @JosePaumard#Stream8 Opérations stateless / stateful Le code suivant : « les noms des personnes apparaissent dans le même ordre que les personnes » ArrayList<Person> persons = ... ; List<String> names = persons.map(Person::getLastName) .collect(Collectors.toList()) ;
  • 211. @JosePaumard#Stream8 Opérations stateless / stateful Le code suivant : « les noms des personnes apparaissent dans le même ordre que les personnes » Parallélisme ? ArrayList<Person> persons = ... ; List<String> names = persons.map(Person::getLastName) .collect(Collectors.toList()) ;
  • 212. @JosePaumard#Stream8 Opérations stateless / stateful Le code suivant : « les noms des personnes apparaissent dans le même ordre que les personnes » Parallélisme ? ArrayList<Person> persons = ... ; List<String> names = persons.map(Person::getLastName) .unordered() .collect(Collectors.toList()) ;
  • 213. @JosePaumard#Stream8 Bilan sur les Stream Un Stream a un état On peut définir des opérations sur un Stream : - intermédiaires & terminales - stateless & stateful Les traitements sur un Stream peuvent être parallélisés
  • 214. @JosePaumard#Stream8 Stream & performance Deux éléments : - traitements lazy - traitements parallèles
  • 215. @JosePaumard#Stream8 Stream & performance Deux éléments : - traitements lazy - traitements parallèles Stream<T> versus IntStream, LongStream, DoubleStream
  • 216. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() .map(Person::getAge) .filter(age -> age > 20) .sum() ;
  • 217. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() // Stream<Person> .map(Person::getAge) .filter(age -> age > 20) .sum() ;
  • 218. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() // Stream<Person> .map(Person::getAge) // Stream<Integer> boxing  .filter(age -> age > 20) .sum() ;
  • 219. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() // Stream<Person> .map(Person::getAge) // Stream<Integer> boxing  .filter(age -> age > 20) // Stream<Integer> re-boxing re- .sum() ;
  • 220. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() // Stream<Person> .map(Person::getAge) // Stream<Integer> boxing  .filter(age -> age > 20) // Stream<Integer> re-boxing re- .sum() ; // pas de méthode sum() sur Stream<T>
  • 221. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() // Stream<Person> .map(Person::getAge) // Stream<Integer> boxing  .filter(age -> age > 20) // Stream<Integer> re-boxing re- .mapToInt(age -> age.getValue()) // IntStream  .sum() ;
  • 222. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() // Stream<Person> .map(Person::getAge) // Stream<Integer> boxing  .filter(age -> age > 20) // Stream<Integer> re-boxing re- .mapToInt(Integer::getValue) // IntStream  .sum() ;
  • 223. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; persons.stream() // Stream<Person> .mapToInt(Person::getAge) // IntStream  .filter(age -> age > 20) // IntStream  // .mapToInt(Integer::getValue) .sum() ;
  • 224. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; int sum = persons.stream() // Stream<Person> .mapToInt(Person::getAge) // IntStream  .filter(age -> age > 20) // IntStream  // .mapToInt(Integer::getValue) .sum() ;
  • 225. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; ??? = persons.stream() // Stream<Person> .mapToInt(Person::getAge) // IntStream  .filter(age -> age > 20) // IntStream  // .mapToInt(Integer::getValue) .max() ;
  • 226. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; OptionalInt opt = persons.stream() // Stream<Person> .mapToInt(Person::getAge) // IntStream  .filter(age -> age > 20) // IntStream  // .mapToInt(Integer::getValue) .max() ;
  • 227. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; ??? = persons.stream() // Stream<Person> .mapToInt(Person::getAge) // IntStream  .filter(age -> age > 20) // IntStream  // .mapToInt(Integer::getValue) .average() ;
  • 228. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple ArrayList<Person> persons = ... ; OptionalInt opt = persons.stream() // Stream<Person> .mapToInt(Person::getAge) // IntStream  .filter(age -> age > 20) // IntStream  // .mapToInt(Integer::getValue) .average() ;
  • 229. @JosePaumard#Stream8 Stream & performance Retour sur l’exemple Calcule en une passe : count, sum, min, max Et donc aussi average ArrayList<Person> persons = ... ; IntSummaryStatistics stats = persons.stream() .mapToInt(Person::getAge) .filter(age -> age > 20) .summaryStatistics() ;
  • 230. @JosePaumard#Stream8 Parallélisation Construction d’un Stream parallèle Construit sur un Fork / Join construit au niveau de la JVM ArrayList<Person> persons = ... ; Stream<Person> stream1 = persons.parallelStream() ; Stream<Person> stream2 = persons.stream().parallel() ;
  • 231. @JosePaumard#Stream8 Parallélisation Construction d’un Stream parallèle Construit sur un Fork / Join construit au niveau de la JVM ArrayList<Person> persons = ... ; Stream<Person> stream1 = persons.parallelStream() ; Stream<Person> stream2 = persons.stream().parallel() ;
  • 232. @JosePaumard#Stream8 Parallélisation On peut imposer deux choses : - le nombre de cœurs sur lequel le FJ Pool va s’installer - Le FJ Pool lui-même !
  • 233. @JosePaumard#Stream8 Parallélisation Par défaut les ForkJoinTask s’exécutent dans le « common pool », un FJ Pool créé au niveau de la JVM Le taux de parallélisme de ce pool est le nombre de cœurs On peut le fixer : System.setProperty( "java.util.concurrent.ForkJoinPool.common.parallelism", 2) ;
  • 234. @JosePaumard#Stream8 Parallélisation On peut aussi imposer le FJ Pool dans lequel les traitements sont faits List<Person> persons = ... ; ForkJoinPool fjp = new ForkJoinPool(2) ; fjp.submit( () -> // persons.stream().parallel() // implémentation de .mapToInt(p -> p.getAge()) // Callable<Integer> .filter(age -> age > 20) // .average() // ).get() ;
  • 236. @JosePaumard#Stream8 Réduction Généralisons la réduction Bien sûr, une réduction peut être vue comme une agrégation au sens SQL (sum, min, max, avg, etc…)
  • 237. @JosePaumard#Stream8 Réduction Généralisons la réduction Bien sûr, une réduction peut être vue comme une agrégation au sens SQL (sum, min, max, avg, etc…) On peut voir les choses de façon plus générale
  • 239. @JosePaumard#Stream8 Réduction Exemple : la somme Un « container » résultat : un entier, de valeur initiale 0
  • 240. @JosePaumard#Stream8 Réduction Exemple : la somme Un « container » résultat : un entier, de valeur initiale 0 Une opération « ajouter » : ajoute un élément au container Une opération « combiner » : fusionner deux containers utilisée si l’on parallélise le traitement
  • 242. @JosePaumard#Stream8 Réduction Remarque sur la valeur initiale Il s’agira de la valeur retournée en cas de réduction sur un ensemble vide !
  • 243. @JosePaumard#Stream8 Réduction Remarque sur la valeur initiale Il s’agira de la valeur retournée en cas de réduction sur un ensemble vide ! … c’est donc l’élément neutre de la réduction …
  • 244. @JosePaumard#Stream8 Réduction Remarque sur la valeur initiale Certaines réduction ont donc un problème : - max,min - average
  • 245. @JosePaumard#Stream8 Réduction Donc une réduction peut se définir par trois opérations : - Création d’un container résultat - Ajout d’un élément à un container - Fusion de deux container
  • 246. @JosePaumard#Stream8 Réduction Modélisation, trois opérations : - constructeur : Supplier - accumulateur : Function - combiner : Function
  • 247. @JosePaumard#Stream8 Réduction Exemple 1 : - constructeur : Supplier - accumulateur : Function - combiner : Function () -> new StringBuffer() ;
  • 248. @JosePaumard#Stream8 Réduction Exemple 1 : - constructeur : Supplier - accumulateur : Function - combiner : Function () -> new StringBuffer() ; (StringBuffer sb, String s) -> sb.append(s) ;
  • 249. @JosePaumard#Stream8 Réduction Exemple 1 : - constructeur : Supplier - accumulateur : Function - combiner : Function () -> new StringBuffer() ; (StringBuffer sb, String s) -> sb.append(s) ; (StringBuffer sb1, StringBuffer sb2) -> sb1.append(sb2) ;
  • 250. @JosePaumard#Stream8 Réduction Exemple 1 : concaténation de chaîne de caractères - constructeur : Supplier - accumulateur : Function - combiner : Function StringBuffer::new StringBuffer::append StringBuffer::append
  • 251. @JosePaumard#Stream8 Réduction : mise en œuvre Exemple 1 : accumulation dans un StringBuffer List<Person> persons = ... ; StringBuffer result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( StringBuffer::new, // constructor StringBuffer::append, // accumulator StringBuffer::append // combiner ) ;
  • 252. @JosePaumard#Stream8 Réduction : collectors Exemple 1 : utilisation d’un collector List<Person> persons = ... ; String result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( Collectors.joining() ) ;
  • 253. @JosePaumard#Stream8 Réduction Exemple 2 : - constructeur : Supplier - accumulateur : Function - combiner : Function () -> new ArrayList() ;
  • 254. @JosePaumard#Stream8 Réduction Exemple 2 : - constructeur : Supplier - accumulateur : Function - combiner : Function () -> new ArrayList() ; (ArrayList list, E e) -> list.add(e) ;
  • 255. @JosePaumard#Stream8 Réduction Exemple 2 : - constructeur : Supplier - accumulateur : Function - combiner : Function () -> new ArrayList() ; (ArrayList list, E e) -> list.add(e) ; (ArrayList list1, ArrayList list2) -> list1.addAll(list2) ;
  • 256. @JosePaumard#Stream8 Réduction Exemple 2 : accumulation dans une liste - constructeur : Supplier - accumulateur : Function - combiner : Function ArrayList::new ArrayList::add ArrayList::addAll
  • 257. @JosePaumard#Stream8 Réduction : mise en œuvre Exemple 2 : accumulation dans une liste List<Person> persons = ... ; ArrayList<String> names = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( ArrayList::new, // constructor ArrayList::add, // accumulator ArrayList::addAll // combiner ) ;
  • 258. @JosePaumard#Stream8 Réduction : mise en œuvre Exemple 2 : accumulation dans une liste List<Person> persons = ... ; ArrayList<String> names = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( ArrayList::new, // constructor Collection::add, // accumulator Collection::addAll // combiner ) ;
  • 259. @JosePaumard#Stream8 Réduction : mise en œuvre Exemple 2 : accumulation dans une liste List<Person> persons = ... ; HashSet<String> names = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( HashSet::new, // constructor Collection::add, // accumulator Collection::addAll // combiner ) ;
  • 260. @JosePaumard#Stream8 Réduction : collectors Exemple 1 : utilisation d’un collector List<Person> persons = ... ; List<String> result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( Collectors.toList() ) ;
  • 261. @JosePaumard#Stream8 Réduction : collectors Exemple 1 : utilisation d’un collector List<Person> persons = ... ; Set<String> result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( Collectors.toSet() ) ;
  • 262. @JosePaumard#Stream8 Réduction : collectors Exemple 1 : utilisation d’un collector List<Person> persons = ... ; TreeSet<String> result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( Collectors.toCollection(TreeSet::new) ) ;
  • 264. @JosePaumard#Stream8 Réduction : collectors Exemple 1 : utilisation d’un collector List<Person> persons = ... ; String result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( Collectors.joining(", ") ) ;
  • 265. @JosePaumard#Stream8 Réduction : collectors Exemple 2 : utilisation d’un collector List<Person> persons = ... ; List<String> result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( Collectors.toList() ) ;
  • 266. @JosePaumard#Stream8 Réduction : collectors Exemple 2 : utilisation d’un collector List<Person> persons = ... ; Set<String> result = persons.stream() .filter(person -> person.getAge() > 20) .map(Person::getLastName) .collect( Collectors.toSet() ) ;
  • 267. @JosePaumard#Stream8 Collectors Classe Collectors : 33 méthodes statiques Boite à outils !
  • 268. @JosePaumard#Stream8 Collectors : groupingBy Table de hachage : age / liste des personnes List<Person> persons = ... ; Map<Integer, List<Person>> result = persons.stream() .collect( Collectors.groupingBy(Person::getAge) ) ;
  • 269. @JosePaumard#Stream8 Collectors : groupingBy Table de hachage : age / nombre de personnes Map<Integer, Long> result = persons.stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.counting() // « downstream collector » ) ) ;
  • 270. @JosePaumard#Stream8 Collectors : groupingBy Table de hachage : age / liste des noms des personnes Map<Integer, List<String>> result = persons.stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.mapping( // Person::getLastName // downstream collector ) // ) ) ;
  • 271. @JosePaumard#Stream8 Collectors : groupingBy Table de hachage : age / liste des noms des personnes Map<Integer, String> result = persons.stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.mapping( // 1er downstream collector Person::getLastName Collectors.joining(", ") // 2ème downstream collector ) ) ) ;
  • 272. @JosePaumard#Stream8 Collectors : groupingBy Table de hachage : age / liste des noms des personnes Map<Integer, TreeSet<String>> result = persons.stream() .collect( Collectors.groupingBy( Person::getAge, Collectors.mapping( Person::getLastName Collectors.toCollection(TreeSet::new) ) ) ) ;
  • 273. @JosePaumard#Stream8 Collectors : groupingBy Table de hachage : age / liste des noms des personnes TreeMap<Integer, TreeSet<String>> result = persons.stream() .collect( Collectors.groupingBy( Person::getAge, TreeMap::new, Collectors.mapping( Person::getLastName Collectors.toCollection(TreeSet::new) ) ) ) ;
  • 274. @JosePaumard#Stream8 Bilan intermédiaire Stream + Collectors = Nouveaux outils pour le map / filter / reduce 1) exécutions lazy 2) exécutions parallèle Des exemples à venir… … mais pour le moment …
  • 276. Java 8 Streams & Collectors Patterns, performance, parallélisation @JosePaumard #Stream8
  • 277. Cas d’utilisation « Houston, we have a problem » https://github.com/JosePaumard/jdk8-lambda-tour
  • 278. @JosePaumard#Stream8 Collectors : groupingBy Fichier CSV contenant les MacDo US -149.95038,61.13712,"McDonalds-Anchorage,AK","3828 W Dimond Blvd, Anchorage,AK, (907) 248-0597" -149.93538,61.18167,"McDonalds-Anchorage,AK","4350 Spenard Rd, Anchorage,AK, (907) 243-5122" http://introcs.cs.princeton.edu/java/data/
  • 279. @JosePaumard#Stream8 Collectors : groupingBy Fichier CSV contenant les MacDo US -149.95038,61.13712,"McDonalds-Anchorage,AK","3828 W Dimond Blvd, Anchorage,AK, (907) 248-0597" -149.93538,61.18167,"McDonalds-Anchorage,AK","4350 Spenard Rd, Anchorage,AK, (907) 243-5122" try (Stream<String> lines = Files.lines(Paths.get("files", "mcdonalds.csv"))) { ... }
  • 280. @JosePaumard#Stream8 McDonald : compter les villes 1ère solution long nTowns = mcdos.stream() .map(McDonald::city) // mcdo -> mcdo.city()
  • 281. @JosePaumard#Stream8 McDonald : compter les villes 1ère solution long nTowns = mcdos.stream() .map(McDonald::city) // mcdo -> mcdo.city() .collect(Collectors.toSet())
  • 282. @JosePaumard#Stream8 McDonald : compter les villes 1ère solution long nTowns = mcdos.stream() .map(McDonald::city) // mcdo -> mcdo.city() .collect(Collectors.toSet()) .size() ;
  • 283. @JosePaumard#Stream8 McDonald : compter les villes 2ème solution : long nTowns = mcdos.stream() .map(McDonald::city) // mcdo -> mcdo.city() .distinct() .collect(Collectors.counting()) ;
  • 284. @JosePaumard#Stream8 McDonald : compter les villes 3ème solution : long nTowns = mcdos.stream() .map(McDonald::city) // mcdo -> mcdo.city() .distinct() .count() ;
  • 285. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 1ère étape : histogramme ville / # de mcdos C’est une table de hachage ! Map<String, Long> map = mcdos.stream() .collect( Collectors.groupingBy( // table de hachage ) ) ;
  • 286. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 1ère étape : histogramme ville / # de mcdos C’est une table de hachage ! Map<String, Long> map = mcdos.stream() .collect( Collectors.groupingBy( McDonald::city, // les clés sont les villes ) ) ;
  • 287. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 1ère étape : histogramme ville / # de mcdos C’est une table de hachage ! Map<String, Long> map = mcdos.stream() .collect( Collectors.groupingBy( McDonald::city, Collectors.counting() // downstream ) ) ;
  • 288. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur
  • 289. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur Il s’agit d’un max
  • 290. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur Il s’agit d’un max - Sur quel ensemble ?
  • 291. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur Il s’agit d’un max - Sur quel ensemble ? - Au sens de quel comparateur ?
  • 292. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur Il s’agit d’un max - Sur quel ensemble ? - L’ensemble des paires clés / valeurs - Au sens de quel comparateur ?
  • 293. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur Il s’agit d’un max - Sur quel ensemble ? - L’ensemble des paires clés / valeurs - Au sens de quel comparateur ? - Le comparateur compare les valeurs
  • 294. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur map.entrySet() // Set des paires clés / valeurs
  • 295. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur map.entrySet() // Set des paires clés / valeurs .stream() .max( // on prend le max ) ;
  • 296. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur map.entrySet() // Set des paires clés / valeurs .stream() .max( // on prend le max Comparator.comparing(entry -> entry.getValue()) ) ;
  • 297. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur map.entrySet() // Set des paires clés / valeurs .stream() .max( // on prend le max Comparator.comparing(Map.Entry::getValue) ) ;
  • 298. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur map.entrySet() // Set des paires clés / valeurs .stream() .max( // on prend le max Map.Entry.comparingByValue() // comparingByKey() ) ;
  • 299. @JosePaumard#Stream8 McDonald : la ville qui en a le plus 2ème étape : extraire la clé associée à la plus grande valeur Optional<Map.Entry<String, Long>> opt = map.entrySet() // Set des paires clés / valeurs .stream() .max( // on prend le max Map.Entry.comparingByValue() // comparingByKey() ) ;
  • 300. @JosePaumard#Stream8 McDonald : n villes qui en ont le plus Plus compliqué… 1) Trier les paires clés / valeurs
  • 301. @JosePaumard#Stream8 McDonald : n villes qui en ont le plus Plus compliqué… 1) Trier les paires clés / valeurs 2) Prendre les n plus grandes
  • 302. @JosePaumard#Stream8 McDonald : la ville qui en a le plus Tri de l’ensemble des paires clés / valeurs map.entrySet() // Set des paires clés / valeurs .stream() .sorted( // tri Comparator.comparing(entry -> -entry.getValue()) )
  • 303. @JosePaumard#Stream8 McDonald : la ville qui en a le plus Tri de l’ensemble des paires clés / valeurs map.entrySet() // Set des paires clés / valeurs .stream() .sorted( // tri Comparator.comparing(entry -> -entry.getValue()) ) .limit(n) // n : nombre de valeurs que l’on veut
  • 304. @JosePaumard#Stream8 McDonald : la ville qui en a le plus Tri de l’ensemble des paires clés / valeurs List<Map.Entry<String, Long>> result = map.entrySet() // Set des paires clés / valeurs .stream() .sorted( // tri Comparator.comparing(entry -> -entry.getValue()) ) .limit(n) // n : nombre de valeurs que l’on veut .collect(Collectors.toList()) ;
  • 305. Cas d’utilisation « Shakespeare joue au Scrabble » https://github.com/JosePaumard/jdk8-lambda-tour
  • 306. @JosePaumard#Stream8 Scrabble Deux fichiers : - les mots autorisés au Scrabble - les mots utilisés par Shakespeare
  • 307. @JosePaumard#Stream8 Scrabble Deux fichiers : - les mots autorisés au Scrabble - les mots utilisés par Shakespeare Un peu de doc (Wikipedia) : private static final int [] scrabbleENScore = { // a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10} ;
  • 308. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot B O N J O U R 3 1 1 8 1 1 1 // lecture du tableau scrabbleENScore
  • 309. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot B O N J O U R 3 1 1 8 1 1 1 // lecture du tableau scrabbleENScore SUM = 16
  • 310. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot B O N J O U R // stream des lettres du mot « bonjour » 3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre SUM = 16
  • 311. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot B O N J O U R // stream des lettres du mot « bonjour » 3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre sum() = 16
  • 312. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot B O N J O U R // stream des lettres du mot « bonjour » 3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre sum() = 16 word -> word.chars() // stream des lettres du mot .map(lettre -> scrabbleENScore[lettre – 'a'])
  • 313. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot B O N J O U R // stream des lettres du mot « bonjour » 3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre sum() = 16 word -> word.chars() // stream des lettres du mot, type IntStream  .map(lettre -> scrabbleENScore[lettre – 'a']) .sum() ; // existe sur IntStream
  • 314. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot B O N J O U R // stream des lettres du mot « bonjour » 3 1 1 8 1 1 1 // mapping : lettre -> score de chaque lettre sum() = 16 Function<String, Integer> score = word -> word.chars() // stream des lettres du mot, type IntStream  .map(lettre -> scrabbleENScore[lettre – 'a']) .sum() ; // existe sur IntStream
  • 315. @JosePaumard#Stream8 Scrabble : score d’un mot 1ère question : calculer le score d’un mot = map / reduce
  • 316. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare
  • 317. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare 1) Histogramme des mots en fonction de leur score
  • 318. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare 1) Histogramme des mots en fonction de leur score 2) max sur les clés
  • 319. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare 1) Histogramme des mots en fonction de leur score  table de hachage : groupingBy 2) max sur les clés  on sait faire
  • 320. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare shakespeareWords.stream() .collect( Collectors.groupingBy( score // fonction de calcul de score ) )
  • 321. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare Map<Integer, List<String>> map = shakespeareWords.stream() .collect( Collectors.groupingBy( score // fonction de calcul de score ) ) ;
  • 322. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare shakespeareWords.stream() .collect( Collectors.groupingBy( score ) // Map<Integer, List<String>> ) .entrySet() .stream() // Map.Entry<Integer, List<String>> .max(Map.Entry.comparingByKey()) ;
  • 323. @JosePaumard#Stream8 Scrabble : score de Shakespeare 2ème question : calculer le meilleur score de Shakespeare Optional<Map.Entry<Integer, List<String>> opt = shakespeareWords.stream() .collect( Collectors.groupingBy( score ) // Map<Integer, List<String>> ) .entrySet() .stream() // Map.Entry<Integer, List<String>> .max(Map.Entry.comparingByKey()) ;
  • 324. @JosePaumard#Stream8 Scrabble : score de Shakespeare 3ème question : limiter aux mots autorisés au Scrabble Optional<Map.Entry<Integer, List<String>> opt = shakespeareWords.stream() .filter(word -> scrabbleWords.contains(word)) .collect( Collectors.groupingBy( score ) // Map<Integer, List<String>> ) .entrySet() .stream() // Map.Entry<Integer, List<String>> .max(Map.Entry.comparingByKey()) ;
  • 325. @JosePaumard#Stream8 Scrabble : score de Shakespeare 3ème question : limiter aux mots autorisés au Scrabble Optional<Map.Entry<Integer, List<String>> opt = shakespeareWords.stream() .filter(scrabbleWords::contains) .collect( Collectors.groupingBy( score ) // Map<Integer, List<String>> ) .entrySet() .stream() // Map.Entry<Integer, List<String>> .max(Map.Entry.comparingByKey()) ;
  • 326. @JosePaumard#Stream8 Scrabble : score de Shakespeare 3ème question : limiter aux mots autorisés au Scrabble = problème de filter / reduce
  • 327. @JosePaumard#Stream8 Scrabble : score de Shakespeare Question : le mot en question est-il possible au Scrabble ?
  • 328. @JosePaumard#Stream8 Scrabble : score de Shakespeare Question : le mot en question est-il possible au Scrabble ? Il n’y a qu’un seul ‘Z’ au Scrabble anglais… private static final int [] scrabbleENDistribution = { // a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 9, 2, 2, 1, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1} ;
  • 329. @JosePaumard#Stream8 Scrabble : score de Shakespeare Question : le mot en question est-il possible au Scrabble ? Il n’y a qu’un seul ‘Z’ au Scrabble anglais… Il faut vérifier le nombre d’utilisation de chaque lettre private static final int [] scrabbleENDistribution = { // a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 9, 2, 2, 1, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1} ;
  • 330. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? B U Z Z A R D // le mot
  • 331. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées
  • 332. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre -> groupingBy
  • 333. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles
  • 334. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles T T F T T T // il faut que tout soit à TRUE -> allMatch
  • 335. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? word -> word.chars() // IntStream  .mapToObj(Integer::new) // Stream<Integer>
  • 336. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? word -> word.chars() // IntStream  .mapToObj(Integer::new) // Stream<Integer> .collect( Collectors.groupingBy( ) ) ;
  • 337. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? word -> word.chars() // IntStream  .mapToObj(Integer::new) // Stream<Integer> .collect( // Map<Integer, Long> Collectors.groupingBy( letter -> letter, Collectors.counting() ) ) ;
  • 338. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? word -> word.chars() // IntStream  .mapToObj(Integer::new) // Stream<Integer> .collect( // Map<Integer, Long> Collectors.groupingBy( Function.identity(), Collectors.counting() ) ) ;
  • 339. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? Function<String, Map<Integer, Long>> letterHisto = word -> word.chars() // IntStream  .mapToObj(Integer::new) // Stream<Integer> .collect( // Map<Integer, Long> Collectors.groupingBy( Function.identity(), Collectors.counting() ) ) ;
  • 340. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? Predicate<String> canWrite = word -> letterHisto .apply(word) // Map<Integer, Long> .entrySet() .stream() // Map.Entry<Integer, Long>
  • 341. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? Predicate<String> canWrite = word -> letterHisto .apply(word) // Map<Integer, Long> .entrySet() .stream() // Map.Entry<Integer, Long> .allMatch( ) ;
  • 342. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? Predicate<String> canWrite = word -> letterHisto .apply(word) // Map<Integer, Long> .entrySet() .stream() // Map.Entry<Integer, Long> .allMatch( entry -> ) ;
  • 343. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? Predicate<String> canWrite = word -> letterHisto .apply(word) // Map<Integer, Long> .entrySet() .stream() // Map.Entry<Integer, Long> .allMatch( entry -> entry.getValue() <= // besoin en lettres ) ;
  • 344. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? Predicate<String> canWrite = word -> letterHisto .apply(word) // Map<Integer, Long> .entrySet() .stream() // Map.Entry<Integer, Long> .allMatch( entry -> entry.getValue() <= // besoin en lettres scrabbleENDistrib[entry.getKey() – 'a'] // lettres dispos ) ;
  • 345. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? Optional<Map.Entry<Integer, List<String>> opt = shakespeareWords.stream() .filter(scrabbleWords::contains) .filter(canWrite) .collect( Collectors.groupingBy( score ) // Map<Integer, List<String>> ) .entrySet() .stream() // Map.Entry<Integer, List<String>> .max(Map.Entry.comparingByKey()) ;
  • 346. @JosePaumard#Stream8 Scrabble : mot possible ? 4ème question : peut-on écrire ce mot ? = problème de map / reduce
  • 347. @JosePaumard#Stream8 Scrabble : et si l’on prend les blancs ? 5ème question : et si l’on utilise les blancs disponibles ?
  • 348. @JosePaumard#Stream8 Scrabble : et si l’on prend les blancs ? 5ème question : et si l’on utilise les blancs disponibles ? Deux impacts :
  • 349. @JosePaumard#Stream8 Scrabble : et si l’on prend les blancs ? 5ème question : et si l’on utilise les blancs disponibles ? Deux impacts : - impact sur les mots que l’on peut faire
  • 350. @JosePaumard#Stream8 Scrabble : et si l’on prend les blancs ? 5ème question : et si l’on utilise les blancs disponibles ? Deux impacts : - impact sur les mots que l’on peut faire - impact sur le calcul du score
  • 351. @JosePaumard#Stream8 Scrabble : et si l’on prend les blancs ? 5ème question : et si l’on utilise les blancs disponibles ? Comment déterminer si un mot est accepté ?
  • 352. @JosePaumard#Stream8 Scrabble : et si l’on prend les blancs ? 5ème question : et si l’on utilise les blancs disponibles ? Comment déterminer si un mot est accepté ? Critère : pas plus de deux blancs
  • 353. @JosePaumard#Stream8 Scrabble : et si l’on prend les blancs ? 5ème question : et si l’on utilise les blancs disponibles ? Comment déterminer si un mot est accepté ? Critère : pas plus de deux blancs On peut écrire une fonction qui calcule le nombre de blancs utilisés pour un mot
  • 354. @JosePaumard#Stream8 Scrabble : mot possible ? 5ème question : et si l’on utilise les blancs disponibles ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles
  • 355. @JosePaumard#Stream8 Scrabble : mot possible ? 5ème question : et si l’on utilise les blancs disponibles ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles 0 0 1 0 0 0 // nombre de blancs nécessaires // il s’agit d’un max !
  • 356. @JosePaumard#Stream8 Scrabble : mot possible ? 5ème question : et si l’on utilise les blancs disponibles ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles 0 0 1 0 0 0 // nombre de blancs nécessaires // il s’agit d’un max ! Integer.max( 0, entry.getValue() - // lettres nécessaires scrabbleEnDistrib[entry.getKey() – 'a'] // lettres dispo
  • 357. @JosePaumard#Stream8 Scrabble : mot possible ? 5ème question : et si l’on utilise les blancs disponibles ? Function<String, Integer> nBlanks = word -> letterHisto.apply(word) .entrySet() .stream() .mapToInt( // IntStream -> sum() dispo Integer.max( 0, entry.getValue() - scrabbleEnDistrib[entry.getKey() – 'a'] ) ) .sum() ;
  • 358. @JosePaumard#Stream8 Scrabble : mot possible ? 5ème question : et si l’on utilise les blancs disponibles ? Optional<Map.Entry<Integer, List<String>> opt = shakespeareWords.stream() .filter(scrabbleWords::contains) .filter(word -> nBlanks.apply(word) <= 2) .collect( Collectors.groupingBy( score ) ) .entrySet() .stream() .max(Map.Entry.comparingByKey()) ;
  • 359. @JosePaumard#Stream8 Scrabble : mot possible ? 5ème question : et si l’on utilise les blancs disponibles ? = problème de map / reduce
  • 360. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ?
  • 361. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? = problème de map / reduce
  • 362. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles 1 1 1 1 1 1 // lettres effectivement utilisées
  • 363. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles 1 1 1 1 1 1 // lettres effectivement utilisées // il s’agit d’un min !
  • 364. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles 1 1 1 1 1 1 // lettres effectivement utilisées * * * * * * 3 1 10 1 1 2 // score individuel de chaque lettre
  • 365. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? B U Z Z A R D // le mot B U Z A R D // les lettres utilisées 1 1 2 1 1 1 // l’occurrence de chaque lettre 2 4 1 9 6 1 // les lettres disponibles 1 1 1 1 1 1 // lettres effectivement utilisées * * * * * * 3 1 10 1 1 2 // score individuel de chaque lettre SUM
  • 366. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? Function<String, Integer> score = word -> letterHisto.apply(word) .entrySet() .stream() .mapToInt( .sum() ;
  • 367. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? Function<String, Integer> score = word -> letterHisto.apply(word) .entrySet() .stream() .mapToInt( entry -> .sum() ;
  • 368. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? Function<String, Integer> score = word -> letterHisto.apply(word) .entrySet() .stream() .mapToInt( entry -> scrabbleENScore[entry.getKey() – 'a']* // score de // la lettre .sum() ;
  • 369. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? Function<String, Integer> score = word -> letterHisto.apply(word) .entrySet() .stream() .mapToInt( entry -> scrabbleENScore[entry.getKey() – 'a']* Integer.min( ) .sum() ;
  • 370. @JosePaumard#Stream8 Scrabble : score avec les blancs ? 6ème question : compter les points avec les blancs ? Function<String, Integer> score = word -> letterHisto.apply(word) .entrySet() .stream() .mapToInt( entry -> scrabbleENScore[entry.getKey() – 'a']* Integer.min( entry.getValue(), // nombre de lettres dans le mot scrabbleENDistrib[entry.getKey() – 'a'] ) .sum() ;
  • 371. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ?
  • 372. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ?
  • 373. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ?
  • 374. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ? 6 solutions pour poser le mot : - utilisation de la case de droite = 3 lettres de la fin du mot - utilisation de la case de gauche = 3 lettres du début
  • 375. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ? Solution : - prendre le max des 3 dernières lettres - avec le max des 3 premières lettres …si le mot fait 7 lettres !
  • 376. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ? Traduit en Java, le max est pris sur que ensemble ? word.chars().skip(4) // 1er flux word.chars().limit(Integer.max(0, word.length() - 4)) // 2ème flux
  • 377. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ? Traduit en Java, le max est pris sur que ensemble ? Le max de ces deux flux, puis le max des ces deux max word.chars().skip(4) // 1er flux word.chars().limit(Integer.max(0, word.length() - 4)) // 2ème flux
  • 378. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ? Traduit en Java, le max est pris sur que ensemble ? Concaténer ces flux ? word.chars().skip(4) // 1er flux word.chars().limit(Integer.max(0, word.length() - 4)) // 2ème flux
  • 379. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ? IntStream.concat( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .map(scrabbleENScore) .max()
  • 380. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : le « lettre compte double » ? Problème : concat ne se parallélise pas bien… IntStream.concat( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .map(scrabbleENScore) .max()
  • 381. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) // retourne un Stream de Stream !
  • 382. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()) .map(scrabbleENScore) .max()
  • 383. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? Que faire de cet Optional ? Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()) .map(scrabbleENScore) .max()
  • 384. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? Que faire de cet Optional ? … qui peut être vide ! Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()) .map(scrabbleENScore) .max()
  • 385. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()) .map(scrabbleENScore) .max() .ifPresent( )
  • 386. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? List<Integer> list = new ArrayList<>() ; Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()) .map(scrabbleENScore) .max() .ifPresent(list::add)
  • 387. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? List<Integer> list = new ArrayList<>() ; Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()) .map(scrabbleENScore) .max() .ifPresent(list::add) ; list.stream().max(Comparator.naturalOrder()).get()
  • 388. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? List<Integer> list = new ArrayList<>() ; list.add(0) ; Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()) .map(scrabbleENScore) .max() .ifPresent(list::add) ; list.stream().max(Comparator.naturalOrder()).get()
  • 389. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? Function<String, Integer> scoreAtPosition = word -> { List<Integer> list = new ArrayList<>() ; list.add(0) ; Stream.of( word.chars().skip(4) word.chars().limit(Integer.max(0, word.length() - 4)) ) .flatMap(Function.identity()).map(scrabbleENScore).max() .ifPresent(list::add) ; return list.stream().max(Comparator.naturalOrder()).get() }
  • 390. @JosePaumard#Stream8 Scrabble : et sur le plateau de jeu ? 7ème question : « lettre compte double » ? = problème de map / reduce !
  • 391. Cas d’utilisation « des films et des acteurs » https://github.com/JosePaumard/jdk8-lambda-tour
  • 392. @JosePaumard#Stream8 Des films et des acteurs Fichier : « movies-mpaa.txt » Contient 14k films américains de 1916 à 2004 Avec : - le titre - l’année de sortie - la liste des acteurs
  • 393. @JosePaumard#Stream8 Des films et des acteurs Fichier : « movies-mpaa.txt » Contient 14k films américains de 1916 à 2004 Avec : - le titre - l‘année de sortie - la liste des acteurs Référence 170k acteurs différents
  • 394. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ?
  • 395. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? Construire une table de hachage : - les clés sont les acteurs - les valeurs sont le nombre de films
  • 396. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? Construction de l’ensemble des acteurs Set<Actor> actors = movies.stream() .flatMap(movie -> movie.actors().stream()) .collect(Collector.toSet()) ;
  • 397. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? actors.stream() .collect( ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 398. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? actors.stream() .collect( Collectors.toMap( ) ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 399. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? actors.stream() .collect( Collectors.toMap( Function.identity(), ) ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 400. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? actors.stream() .collect( Collectors.toMap( Function.identity(), actor -> movies.stream() .filter(movie -> movie.actors().contains(actor)) .count() ) ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 401. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? actors.stream().parallel() // T = 40s  .collect( Collectors.toMap( Function.identity(), actor -> movies.stream() .filter(movie -> movie.actors().contains(actor)) .count() ) ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 402. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? movies.stream() .map(movie -> movie.actors().stream()) // stream de stream !
  • 403. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? movies.stream() .flatMap(movie -> movie.actors().stream()) // stream d’acteurs .collect( )
  • 404. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? movies.stream() .flatMap(movie -> movie.actors().stream()) // stream d’acteurs .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) )
  • 405. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? movies.stream() .flatMap(movie -> movie.actors().stream()) // stream d’acteurs .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 406. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ?
  • 407. @JosePaumard#Stream8 Des films et des acteurs 1ère question : quel acteur a joué dans le plus de films ? Réponse : Frank Welker
  • 408. @JosePaumard#Stream8 Des films et des acteurs 2ème question : idem, en une année ?
  • 409. @JosePaumard#Stream8 Des films et des acteurs 2ème question : idem, en une année ? movies.stream() .flatMap(movie -> movie.actors().stream()) // stream d’acteurs .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 410. @JosePaumard#Stream8 Des films et des acteurs 2ème question : idem, en une année ? movies.stream() .flatMap(movie -> movie.actors().stream()) // stream d’acteurs .collect( Collectors.groupingBy( Function.identity(), // construire une table de hachage année / nombre ) ) .entrySet().stream() .max(Map.Entry.comparingByValue()).get() ;
  • 411. @JosePaumard#Stream8 Des films et des acteurs 2ème question : idem, en une année ? On peut construire une table de hachage : - les clés sont les années - les valeurs sont … des tables de hachage