149 votes

Compréhension de Spliterator, Collector et Stream en Java 8

Je rencontre des difficultés à comprendre l'interface Stream en Java 8, surtout en ce qui concerne les interfaces Spliterator et Collector. Mon problème est que je ne parviens pas à comprendre Spliterator et les interfaces Collector encore, et en conséquence, l'interface Stream m'est encore quelque peu obscure.

Qu'est-ce qu'un Spliterator et un Collector, et comment puis-je les utiliser? Si je suis prêt à écrire mon propre Spliterator ou Collector (et probablement mon propre Stream dans le processus), que devrais-je faire et ne pas faire?

J'ai lu quelques exemples éparpillés sur le web, mais comme tout est encore nouveau et sujet à des changements, les exemples et tutoriels sont encore très rares.

148voto

Louis Wasserman Points 67557

Vous ne devriez presque certainement jamais avoir à traiter avec Spliterator en tant qu'utilisateur; il ne devrait être nécessaire que si vous écrivez vos propres types de Collection et avez également l'intention d'optimiser les opérations parallèles sur ceux-ci.

Pour ce que ça vaut, un Spliterator est une façon de parcourir les éléments d'une collection de manière à pouvoir facilement diviser une partie de la collection, par exemple parce que vous parallélisez et que vous voulez qu'un thread travaille sur une partie de la collection, un autre thread travaille sur une autre partie, etc.

Vous ne devriez pratiquement jamais enregistrer des valeurs de type Stream dans une variable, non plus. Stream est un peu comme un Iterator, dans la mesure où c'est un objet à usage unique que vous utiliserez presque toujours dans une chaîne fluide, comme dans l'exemple de la Javadoc :

int somme = widgets.stream()
                  .filter(w -> w.getCouleur() == ROUGE)
                  .mapToInt(w -> w.getPoids())
                  .sum();

Collector est la version la plus généralisée et abstraite possible d'une opération de "réduction" à la map/reduce ; en particulier, il doit prendre en charge les étapes de parallélisation et de finalisation. Les exemples de Collector incluent :

  • la somme, par exemple Collectors.reducing(0, (x, y) -> x + y)
  • l'ajout dans un StringBuilder, par exemple Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)

32 votes

Spliterator(s) offre également un moyen de diffuser un Iterable qui n'est pas une Collection

2 votes

Je voulais dire "une opération de réduction, au sens où ce terme est entendu dans map/reduce"

1 votes

Est-ce que Collectors.of est une ancienne méthode de la version bêta qui a été supprimée ou est-ce que je rate quelque chose ? Pour complétude, (x, y) -> x+y peut être écrit comme Integer::sum.

91voto

Thomas W Points 7179

Spliterator signifie essentiellement "Iterator fragmentable".

Un seul fil peut traverser/traiter l'ensemble du Spliterator lui-même, mais le Spliterator a également une méthode trySplit() qui va "décomposer" une section pour quelqu'un d'autre (généralement, un autre fil) de traiter - laissant le Spliterator actuel avec moins de travail.

Collector combine la spécification d'une fonction reduce (de la renommée map-reduce), avec une valeur initiale, et une fonction pour combiner deux résultats (permettant ainsi aux résultats de flux de travail Spliterés d'être combinés.)

Par exemple, le Collector le plus basique aurait une valeur initiale de 0, ajouterait un entier à un résultat existant, et 'combinerait' deux résultats en les ajoutant. Ainsi, en additionnant un flux splitté d'entiers.

Voir :

0 votes

Une valeur pour combiner deux résultats?

0 votes

@JasonLaw -- clarifié ! Merci pour la suggestion.

5voto

Interface Spliterator - est une fonctionnalité essentielle des Streams.

Les méthodes par défaut stream() et parallelStream() sont présentées dans l'interface Collection. Ces méthodes utilisent le Spliterator via l'appel à la méthode spliterator():

...

default Stream stream() {
    return StreamSupport.stream(spliterator(), false);
}

default Stream parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}

...

Spliterator est un itérateur interne qui divise le stream en parties plus petites. Ces parties plus petites peuvent être traitées en parallèle.

Entre autres méthodes, il y a deux plus importantes à comprendre le Spliterator:

  • boolean tryAdvance(Consumer action) Contrairement à l'Iterator, il tente d'exécuter l'opération avec l'élément suivant. Si l'opération est exécutée avec succès, la méthode renvoie true. Sinon, elle renvoie false - ce qui signifie qu'il n'y a pas d'élément ou que c'est la fin du stream.

  • Spliterator trySplit() Cette méthode permet de diviser un ensemble de données en plusieurs ensembles plus petits selon un critère quelconque (taille du fichier, nombre de lignes, etc).

4voto

Ajay Points 1656

Voici des exemples d'utilisation des collecteurs prédéfinis pour effectuer des tâches de réduction mutable courantes :

 // Accumuler les noms dans une liste
 List list = people.stream().map(Person::getName).collect(Collectors.toList());

 // Accumuler les noms dans un TreeSet
 Set set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));

 // Convertir les éléments en chaînes de caractères et les concaténer, séparés par des virgules
 String joined = things.stream()
                       .map(Object::toString)
                       .collect(Collectors.joining(", "));

 // Calculer la somme des salaires des employés
 int total = employees.stream()
                      .collect(Collectors.summingInt(Employee::getSalary)));

 // Regrouper les employés par service
 Map> byDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment));

 // Calculer la somme des salaires par service
 Map totalByDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                                               Collectors.summingInt(Employee::getSalary)));

 // Partitionner les étudiants en réussite et en échec
 Map> passingFailing =
     students.stream()
             .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X