web-dev-qa-db-fra.com

Comprendre Spliterator, Collector et Stream dans Java 8

J'ai de la difficulté à comprendre l'interface Stream dans Java 8, en particulier lorsqu'il s'agit de l'interface Spliterator et Collector interfaces. Mon problème est que je ne comprends tout simplement pas encore les interfaces Spliterator et Collector, de sorte que l'interface Stream est encore quelque peu obscure pour moi.

En quoi consistent exactement Spliterator et Collector et comment puis-je les utiliser? Si je suis prêt à écrire mon propre Spliterator ou Collector (et probablement mon propre Stream dans ce processus), que dois-je faire et ne pas faire?

J'ai lu quelques exemples éparpillés sur le Web, mais comme tout est nouveau et sujet à modifications, les exemples et les didacticiels sont encore très rares.

134
Victor Stafusa

Vous ne devriez presque certainement jamais avoir à traiter Spliterator en tant qu'utilisateur; cela ne devrait être nécessaire que si vous écrivez Collection tapez vous-même et aussi l'intention d'optimiser les opérations parallélisées sur ceux-ci.

Pour ce qui en vaut la peine, une Spliterator est un moyen de manipuler les éléments d'une collection de manière à pouvoir en séparer une partie, par exemple. parce que vous parallélisez et que vous voulez qu'un thread fonctionne sur une partie de la collection, un thread sur une autre, etc.

En principe, vous ne devriez jamais non plus enregistrer de valeurs de type Stream dans une variable. Stream est un peu comme un Iterator, en ce sens qu'il s'agit d'un objet à usage unique que vous utiliserez presque toujours dans une chaîne courante, comme dans l'exemple Javadoc:

int sum = widgets.stream()
                  .filter(w -> w.getColor() == RED)
                  .mapToInt(w -> w.getWeight())
                  .sum();

Collector est la version abstraite la plus généralisée possible d'une opération "réduire" à la carte/réduire; en particulier, il doit prendre en charge les étapes de parallélisation et de finalisation. Exemples de Collectors comprennent:

  • la sommation, par exemple Collectors.reducing(0, (x, y) -> x + y)
  • Ajout de StringBuilder, par exemple Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)
136
Louis Wasserman

Spliterator signifie fondamentalement "Itérateur divisible".

Un seul thread peut traverser/traiter l'intégralité du Spliterator lui-même, mais celui-ci a également une méthode trySplit() qui "divise" une section à traiter par quelqu'un d'autre (généralement un autre thread), laissant ainsi moins de travail au spliter actuel.

Collector combine la spécification d'une fonction reduce (de la réputation de carte-réduction) avec une valeur initiale et une valeur permettant de combiner deux résultats (ce qui permet de combiner les résultats de flux de travail Spliterated). )

Par exemple, le collecteur le plus élémentaire aurait une valeur initiale égale à 0, ajouterait un entier à un résultat existant et "combinerait" deux résultats en les ajoutant. En sommant ainsi un flux fractionné d’entiers.

Voir:

86
Thomas W

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

 // Accumulate names into a List
 List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());

 // Accumulate names into a TreeSet
 Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));

 // Convert elements to strings and concatenate them, separated by commas
 String joined = things.stream()
                       .map(Object::toString)
                       .collect(Collectors.joining(", "));

 // Compute sum of salaries of employee
 int total = employees.stream()
                      .collect(Collectors.summingInt(Employee::getSalary)));

 // Group employees by department
 Map<Department, List<Employee>> byDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment));

 // Compute sum of salaries by department
 Map<Department, Integer> totalByDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                                               Collectors.summingInt(Employee::getSalary)));

 // Partition students into passing and failing
 Map<Boolean, List<Student>> passingFailing =
     students.stream()
             .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
6
Ajay Kumar

Interface Spliterator - est une fonctionnalité essentielle de Streams .

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

...

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

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

...

Spliterator est un itérateur interne qui décompose le flux en parties plus petites. Ces petites pièces peuvent être traitées en parallèle.

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

  • boolean tryAdvance(Consumer<? super T> action) Contrairement à Iterator , il tente d'effectuer l'opération avec l'élément suivant. Si l'opération est exécutée avec succès, la méthode retourne true. Sinon, retourne false - cela signifie qu'il y a absence d'élément ou de fin du flux.

  • Spliterator<T> trySplit() Cette méthode permet de fractionner un ensemble de données en un ensemble beaucoup plus petit en fonction d’un critère ou d’un autre (taille du fichier, nombre de lignes, etc.).

2
Aleksey Bykov