2. antislashn.org Java 8 - collections et stream 2 / 29
Introduction
● Quelque soit le langage, l'utilisation des collections est
incontournable
● tableau, liste, map, pile, ...
● Permettent de regrouper des donnée et de les traiter
● liste des factures d'un client pour calculer la moyenne de
ses paiements
● les traitements sont effectués en Java
3. antislashn.org Java 8 - collections et stream 3 / 29
Introduction
● Une première approche du modèle de conception
d'un traitement pourrait-être comme en SQL
● rechercher
● grouper
● exemple
SELECT AVG(montant) FROM factures WHERE facture.fk=?
– le développeur n'a pas besoin de coder comment calculer la
moyenne des montants facturés pour un client
4. antislashn.org Java 8 - collections et stream 4 / 29
Introduction
● En java il n'y as pas (n'avait pas) de moyen autre que
de coder dans une boucle le traitement
● De plus : comment optimiser les traitements sur des
collections importantes
● comment répartir les traitements sur une collection sur les
processeurs multi-cœurs
– il existe des solutions : framework mutithreading
● cf Java 7 avec fork-join
– mais le codage de traitements parallèle peut être complexe et
difficile à mettre au point
5. antislashn.org Java 8 - collections et stream 5 / 29
Introduction
● Java 8 : nouvelle API appelée Stream
● traitement des données de manière déclarative
● utilisation possible des architectures multi-cœurs
– utilisation de parallelStream() au lien de stream()
● comme beaucoup d'autres, Stream utilise la librairie
Optional
6. antislashn.org Java 8 - collections et stream 6 / 29
Optional
● Objectifs :
● solution au NullPointerException
● initié dans Google Guava
● Optional est un container contenant ou non une
valeur
● isPresent() renvoie true si la valeur est présente
● get() renvoie la valeur
● plusieurs autre méthodes permettent de retourner ou
exécuter des traitements si la valeur n'est pas présente
8. antislashn.org Java 8 - collections et stream 8 / 29
Exemple
● Programmation classique en Java
//Recherche des tarifs pour la destination cible
Destination destination = null;
for(Destination d : destinations) {
if(d.getRegion().equals(destinationCible)) {
destination = d;
break;
}
}
// tri des dates de voyages par prix
Collections.sort(destination.getDatesVoyages(), new Comparator<DatesVoyage>() {
@Override
public int compare(DatesVoyage dv1, DatesVoyage dv2) {
return (int)(dv1.getPrixHT() - dv2.getPrixHT());
}
});
// extraction des identiants des dates de voyages
List<Double> identifiants = new ArrayList<>();
for(DatesVoyage dv : destination.getDatesVoyages()) {
identifiants.add(dv.getPrixHT());
}
9. antislashn.org Java 8 - collections et stream 9 / 29
Exemple
● Utilisation des Streams de Java 8
List<Integer> datesVoyageID = destinations.stream()
.filter(d -> d.getRegion().equals(destinationCible))
.flatMap(d -> d.getDatesVoyages().stream())
.sorted(Comparator.comparing(DatesVoyage::getPrixHT))
.map(DatesVoyage::getId)
.collect(Collectors.toList());
destinations
filter
prédicat
flatMap
fonction
datesVoyages
sorted
fonction fonction
collectmap
10. antislashn.org Java 8 - collections et stream 10 / 29
Principes de base
● Stream
● séquence d'éléments provenant d'une source et
supportant des opérations de regroupement
– séquence d'élément : un stream permet l'accès à un ensemble
de valeurs d'un type T
● le stream est l'interface d'accès aux éléments, pas la collection d'éléments
– source : le stream consomme les éléments provenant d'une
source
● collection, tableau, I/O
– opération de regroupement : à l'image de SQL, le stream fournit
des opérations de base
● filter, map, reduce, find, match, sorted, ...
11. antislashn.org Java 8 - collections et stream 11 / 29
Principes de base
● Les streams ont deux caractéristiques qui les
différencient des opérations sur les collections
● chaînage (pipelining) : beaucoup d'opérations sur les
streams retournent eux même un stream
– ce qui permet de chaîner les streams entre-eux
– permet un certains nombres d'optimisation
● mode lazy, court-circuit
● itération interne
– contrairement aux collections qui nécessitent une itération
explicite (externe), les streams effectuent les itérations de
manière cachée
12. antislashn.org Java 8 - collections et stream 12 / 29
Streams vs Collections
● Les collections gèrent des données
● Les streams gèrent des traitements
● La différence entre collections et stream est :
● quand les traitements sont-ils appliqués ?
13. antislashn.org Java 8 - collections et stream 13 / 29
Streams vs Collections
● L'utilisation d'une collection nécessite une itération
● Un stream utilise une itération interne
List<String> transactionIds = new ArrayList<>();
for(Transaction t: transactions){
transactionIds.add(t.getId());
}
List<Integer> transactionIds =
transactions.stream()
.map(Transaction::getId)
.collect(Collectors.toList());
14. antislashn.org Java 8 - collections et stream 14 / 29
Opérations sur les streams
● Deux catégories d'opérations
● opérations intermédiaires
– renvoient un stream
– elles peuvent être connectées les unes aux autres
● opérations terminales
– renvoient un résultat (ou void)
● Les opérations intermédiaires ne exécutées que si
une opération terminale est invoquée
● mode lazy
15. antislashn.org Java 8 - collections et stream 15 / 29
Opérations sur les streams
● Exemple
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> twoEvenSquares =
numbers.stream()
.filter(n -> {
System.out.println("filtering " + n);
return n % 2 == 0;
})
.map(n -> {
System.out.println("mapping " + n);
return n * n;
})
.limit(2)
.collect(toList());
filtering 1
filtering 2
mapping 2
filtering 3
filtering 4
mapping 4
court-circuit : une partie
du stream est traitée
les opérations filter et
map ont été regroupées
en un seul passage
17. antislashn.org Java 8 - collections et stream 17 / 29
Opérations sur les streams
● Pour résumer, les streams nécessitent
● une source de données sur laquelle sera effectuée une
requête
● un chaînage d'opérations intermédiaires
● une opération terminale qui déclenche l'exécution de la
chaîne d'opérations intermédiaires
18. antislashn.org Java 8 - collections et stream 18 / 29
Opérations de filtrage
● filter(Predicate) : retourne un stream incluant
tous les éléments correspondant au prédicat
● distinct : retourne un stream avec des éléments
uniques
● implémentation de la méthode equals
● limit(n) : retourne un stream limité à la taille n
● skip(n) : retourne un stream dont les n premiers
éléments ne sont pas pris en compte
19. antislashn.org Java 8 - collections et stream 19 / 29
Opérations de recherche
● anyMatch(Predicate) : retourne vrai si un des
éléments du stream correspond au prédicat
● allMatch(Predicate) : retourne vrai si tous les
éléments du stream correspondent au prédicat
● noneMatch(Predicate) : retourne vrai si aucun
élément du stream ne correspond au prédicat
● findFirst(), findAny() : retourne un
Optional<T> sur le premier élément, ou un élément
quelconque
20. antislashn.org Java 8 - collections et stream 20 / 29
Opération de recherche
● findAny() findFirst()
● opérations court-circuit
● retournent Optional<T>
● sur des traitements parallèles, findFirst() retourne le
premier élément
– findAny() ne garanti pas le retour du premier élément
The behavior of this operation is explicitly nondeterministic; it is free to select any element in the stream. This is to
allow for maximal performance in parallel operations; the cost is that multiple invocations on the same source may not
return the same result. (If a stable result is desired, use findFirst() instead.)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
numbers.stream()
.filter(t -> t>4)
.findAny()
.ifPresent(System.out::println);
sortie console :
5
21. antislashn.org Java 8 - collections et stream 21 / 29
Mapping
● Permet de transposer les éléments d'un stream dans
un autre stream
● une fonction permet de mapper chaque élément, en lui
appliquant une fonction, vers un nouvel élément
● retourne un stream
List<Integer> test = numbers.stream()
.filter(t -> t>4)
.map(t -> t*2) // multiplication par 2
.collect(toList());
22. antislashn.org Java 8 - collections et stream 22 / 29
Mapping
● T reduce(T identity, BinaryOperator<T> accumulator)
● permet de réduire le nombre d'éléments en appliquant une
fonction sur deux éléments consécutifs
● paramètres
– une valeur initiale
– une fonction combinant des éléments pour produire une
nouvelle valeur
int somme = numbers.stream()
.reduce(0,(a,b) -> (a+b)); // somme des éléments, 0 : valeur de départ
23. antislashn.org Java 8 - collections et stream 23 / 29
Streams numériques
● L'utilisation de la méthode reduce() sur les types
primitifs est coûteuses : nombreuses transformations
en objets
● int ↔ Integer, double ↔ Double, ...
● Il existe des streams spécialisés dans les
numériques
● IntStream, DoubleStream, LongStream
● Méthodes usuelles pour convertir un stream
générique vers un stream numérique :
● mapToInt, mapToDouble, mapToLong
25. antislashn.org Java 8 - collections et stream 25 / 29
Streams numériques
● D'autres fonctions utilitaires existent
● cf. javadoc
● Exemple : création d'un stream de nombres impairs
compris entre 10 et 30
IntStream intStream = IntStream.range(10, 30).filter(n -> n%2 == 1);
intStream.forEach(System.out::println);
26. antislashn.org Java 8 - collections et stream 26 / 29
Créer un stream
● Méthodes utilitaires
● Arrays.stream
● Stream.of(...)
● Files.lines
String fileName = "monopoly.txt";
long nbLines = Files.lines(Paths.get(fileName)).count();
System.out.println("Nombre de lignes : " +nbLines);
27. antislashn.org Java 8 - collections et stream 27 / 29
Créer un stream
● Création à partir de fonctions
● les éléments d'un stream sont créés à la demande
● Stream.iterate et Stream.generate permettent de
créer un stream à partir d'une fonction
Stream<Integer> ints = Stream.iterate(1, i->i+2);
ints.limit(10).forEach(System.out::println);
Stream<Double> doubles = Stream.generate(new Random()::nextDouble);
doubles.limit(10).forEach(System.out::println);