Grundlegendes zu Spliterator, Collector und Stream in Java 8

143

Ich habe Probleme, die StreamSchnittstelle in Java 8 zu verstehen , insbesondere dort, wo sie mit den Schnittstellen Spliteratorund zu tun hat Collector. Mein Problem ist, dass ich Spliteratordie CollectorSchnittstellen einfach noch nicht verstehen kann und die Schnittstelle daher Streamfür mich immer noch etwas dunkel ist.

Was genau ist a Spliteratorund a Collectorund wie kann ich sie verwenden? Was soll ich tun und was nicht, wenn ich bereit bin, mein eigenes Spliteratoroder Collector(und wahrscheinlich mein eigenes Streamin diesem Prozess) zu schreiben ?

Ich habe einige Beispiele gelesen, die im Internet verstreut sind, aber da hier alles noch neu ist und Änderungen unterliegt, sind Beispiele und Tutorials immer noch sehr spärlich.

Victor Stafusa
quelle

Antworten:

142

Sie sollten sich mit ziemlicher Sicherheit nie Spliteratorals Benutzer befassen müssen . es ist nur notwendig, wenn Sie schreiben CollectionTypen selbst und auch zu optimieren beabsichtigen Operationen auf ihnen parallelisiert.

Für das, was es wert ist, Spliteratorist a eine Möglichkeit, die Elemente einer Sammlung so zu bearbeiten, dass es einfach ist, einen Teil der Sammlung abzuspalten, z. B. weil Sie parallelisieren und möchten, dass ein Thread an einem Teil der Sammlung arbeitet. ein Thread, um an einem anderen Teil usw. zu arbeiten

Sie sollten im Wesentlichen auch niemals Werte vom Typ Streamin einer Variablen speichern . Streamist insofern wie ein IteratorObjekt, als es sich um ein einmaliges Objekt handelt, das Sie fast immer in einer fließenden Kette verwenden, wie im Javadoc-Beispiel:

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

Collectorist die allgemeinste, abstrakteste mögliche Version einer "Reduktions" -Operation a la Map / Reduce; Insbesondere müssen Parallelisierungs- und Finalisierungsschritte unterstützt werden. Beispiele für Collectors sind:

  • Summieren, z Collectors.reducing(0, (x, y) -> x + y)
  • StringBuilder anhängen, z Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)
Louis Wasserman
quelle
31
Spliterator (s) bietet auch eine Möglichkeit, ein Iterable zu streamen, das keine Sammlung ist
Bohemian
2
Ich meinte "eine Reduktionsoperation in dem Sinne, dass der Begriff in Karte / Reduktion gemeint ist"
Louis Wasserman
1
Ist Collectors.ofeine alte Methode der Beta-Version entfernt worden oder fehlt mir etwas? Der Vollständigkeit halber (x,y) -> x+ykann geschrieben werden als Integer::sum.
Jean-François Savard
3
Äh, nein, tut mir leid, es ist Collector.of, nicht Collectors.of.
Louis Wasserman
2
Ihr Beispiel für Collector wäre nützlicher, wenn Sie erklären würden, was jeder Ihrer Collectors tut.
MiguelMunoz
90

Spliterator bedeutet im Grunde "spaltbarer Iterator".

Ein einzelner Thread kann den gesamten Spliterator selbst durchlaufen / verarbeiten, aber der Spliterator verfügt auch über eine Methode, mit trySplit()der ein Abschnitt für eine andere Person (normalerweise einen anderen Thread) "abgespalten" wird, sodass der aktuelle Spliterator weniger Arbeit benötigt.

Collectorkombiniert die Spezifikation einer reduceFunktion (von Map-Reduce-Ruhm) mit einem Anfangswert und einer Funktion zum Kombinieren von zwei Ergebnissen (wodurch die Ergebnisse von Spliterated-Arbeitsströmen kombiniert werden können).

Zum Beispiel würde der grundlegendste Collector eine anfängliche vaue von 0 haben, eine Ganzzahl zu einem vorhandenen Ergebnis hinzufügen und zwei Ergebnisse durch Hinzufügen 'kombinieren'. Auf diese Weise wird ein spliterierter Strom von ganzen Zahlen summiert.

Sehen:

Thomas W.
quelle
ein Wert, um zwei Ergebnisse zu kombinieren?
Jason Law
@ JasonLaw - geklärt! Danke für den Vorschlag.
Thomas W
5

Im Folgenden finden Sie Beispiele für die Verwendung der vordefinierten Kollektoren zur Ausführung allgemeiner veränderlicher Reduktionsaufgaben:

 // 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));
Ajay Kumar
quelle
2
Dies beantwortet nicht die Frage des Op, und es gibt keine Erklärung oder Beschreibung Ihres Beitrags.
Sid
4

Schnittstelle Spliterator- ist eine Kernfunktion von Streams .

Die stream()und parallelStream()Standardmethoden werden in der CollectionBenutzeroberfläche dargestellt. Diese Methoden verwenden den Spliterator über den Aufruf von spliterator():

...

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

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

...

Spliterator ist ein interner Iterator, der den Stream in kleinere Teile aufteilt. Diese kleineren Teile können parallel bearbeitet werden.

Unter anderem gibt es zwei wichtige Methoden, um den Spliterator zu verstehen:

  • boolean tryAdvance(Consumer<? super T> action) Im Gegensatz zu Iteratorversucht es, die Operation mit dem nächsten Element auszuführen. Wenn die Operation erfolgreich ausgeführt wurde, gibt die Methode zurück true. Andernfalls wird zurückgegeben false- das bedeutet, dass kein Element oder kein Ende des Streams vorhanden ist.

  • Spliterator<T> trySplit() Diese Methode ermöglicht es, einen Datensatz nach dem einen oder anderen Kriterium (Dateigröße, Anzahl der Zeilen usw.) in viele kleinere Sätze aufzuteilen.


quelle
´Wenn die Operation erfolgreich ausgeführt wurde..´ Sie sollten dies wahrscheinlich umformulieren. tryAdvance javadoc ist klarer: ´Wenn ein verbleibendes Element vorhanden ist, führt es die angegebene Aktion aus und gibt true zurück. sonst gibt false zurück.´
Piro sagt Reinstate Monica