Informationen zur Java8-Stream-Reduzierung

In Java8 Stream Rough Summary habe ich die meisten Stream-Operationen vorgestellt, aber ich werde über die Reduktionsoperationen schreiben, da anscheinend eine detaillierte Erklärung erforderlich ist. Grundsätzlich verweise ich auf JavaDoc of Stream.

Was ist ein Reduktionsvorgang?

Eine Reduktionsoperation ist eine Operation, die das Ergebnis der Kombination aller Elemente in einem Stream unter Verwendung einer kumulativen Funktion zu einem zurückgibt. Es scheint, dass Reduktion auf Japanisch Faltung genannt wird, aber es ist keine sogenannte Faltung. Es ist eher eine Gesamt- oder Gesamtleistung (oder besser gesagt, eine Gesamt- oder Gesamtleistung ist eine Art von Reduzierung). Reduktionsvorgänge umfassen Reduzierungen, die einen einzelnen Wert zurückgeben, und variable Reduzierungen, die einen Container zurückgeben, der mehrere Werte enthält (z. B. eine Sammlung).

reduzieren ~ reduzieren ~

Die Methode "Reduzieren" gibt das Ergebnis der Akkumulation jedes Elements mithilfe einer kumulativen Funktion ("Akkumulator") zurück. Es gibt drei Arten von Überschreibungen bei der "Reduzieren" -Methode:

Methode
T reduce(T identity, BinaryOperator<T> accumulator)
Optional<T> reduce(BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)

T: Elementtyp in Stream

Jedes Argument

Akkumulator ~ Kumulative Funktion ~

"Akkumulator" ist eine Funktion (Schnittstelle) zur Berechnung des kumulativen Ergebnisses durch Wiederholung der Summierung von Elementen. Bei der "Reduce" -Methode wird "accumulator.apply" intern aufgerufen, um die beiden Werte zu addieren und ein Zwischenergebnis zu generieren, und der Summierungsvorgang wird für das Zwischenergebnis wiederholt, um das Endergebnis zu erhalten. .. Daher muss "accumulator.apply" eine Operation sein, für die das Kombinationsgesetz gilt.

Identität ~ Einheitselement ~

indetify ist der Einheitsquellenwert für Accelerator.apply.

Mathematische Definition des Einheitselements

Beliebiges Element a in der Menge und Binomialoperationen darauf*E, das die folgenden Eigenschaften erfüllt, wird als Einheitselement bezeichnet.
a * e = e * a = a

Java-ähnlicher Ausdruck

Eine Identifikation, die die folgenden Eigenschaften für jedes Element a im Stream erfüllt, wird als Einheitselement bezeichnet.
accumulator.apply(identify, a) == accumulator.apply(a, identify) == a

Um ein konkretes Beispiel zu geben, ist das Einheitselement von + (zusätzlich) in der reellen Zahlenmenge 0 und das Einheitselement von * (Multiplikator) 1.

Wenn a = 2

0 + 2 = 2 + 0 = 2
1 * 2 = 2 * 1 = 2

Wenn im Stream kein Element vorhanden ist, wird der ursprüngliche Wert aufgrund der Reduzierung zurückgegeben. Wenn Sie kein Einheitenelement angeben (zweite Überschreibungsmethode), sollten Sie es so weit wie möglich angeben, da dies den Prozess innerhalb von "Reduzieren" vereinfacht (insbesondere für die parallele Verarbeitung).

Kombinierer ~ Kombinationsfunktion ~

combiner ist eine Funktion (Schnittstelle) zum Kombinieren von kumulativen Ergebnissen. Kombinieren Sie in parallelen Streams die in jedem Thread ausgeführten Ergebnisse. Wenn Sie nicht "Kombinierer" angeben (erste und zweite Überschreibungsmethode), wird dieser Verknüpfungsprozess von "Akkumulator" ausgeführt. Auch für sequentielle Streams wird dieser nicht verwendet, selbst wenn Sie "Kombinierer" angeben.

"Kombinierer" muss mit "Akkumulator" identisch sein, das Einheitselement muss "Identität" sein und es muss eine Funktion sein, die dem verbindlichen Gesetz entspricht, und es muss Folgendes erfüllen (kompatibel mit "Akkumulator").

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

Der Fall, in dem "Kombinierer" erforderlich ist, ist, wenn die Funktionen, die beim Summieren der Elemente und beim Kombinieren der kumulativen Ergebnisse ausgeführt werden sollen, unterschiedlich sind. Zum Beispiel ist die Summe der Quadrate der Fall. Bei der Quadratsumme ist es $ a ^ 2 + b ^ 2 $ zwischen Elementen, aber wenn Sie beim Kombinieren der Ergebnisse dieselbe Funktion verwenden, ist $ (a ^ 2 + b ^ 2) ^ 2 + ( Es wird c ^ 2 + d ^ 2) ^ 2 $ sein. Daher kann die Verringerung der Quadratsumme realisiert werden, indem "Kombinierer" zu einer einfachen Summe gemacht wird.

Stream.iterate(1, x->x+1)
      .limit(4)
      .parallel()
      .reduce(0, (x,y)->x*x + y*y, (x,y)->x+y);

Bei diesem Vorgang wird die Summe der Quadrate von 1 bis 4 verwendet, sodass das Ergebnis 30 ist. Wenn Sie jedoch keinen Kombinierer verwenden, ist das Ergebnis anders. Da es schwierig ist, die Berechnungsreihenfolge bei der Parallelverarbeitung zu überprüfen, lautet das Ergebnis 1172, wenn Sie zur sequentiellen Verarbeitung wechseln und diese Verarbeitung ausführen. Dies ist das Ergebnis der folgenden Berechnungen:

(((((0^2+1^2)^2)+2^2)^2+3^2)^2+4^2) = 1172

Diese Form der Verarbeitung kann jedoch einfach mit "map" ausgedrückt werden. Für die Summe der Quadrate müssen Sie nur das Quadrat jedes Elements mit "map" finden und dann reduzieren.

Stream.iterate(1, x->x+1)
      .limit(4)
      .parallel()
      .map(x->x*x)
      .reduce(0, (x,y)->x*x + y*y);

sammle ~ variable Reduktion ~

Die Methode "collect" gibt als Ergebnis einen veränderlichen Ergebniscontainer anstelle eines Werts zurück. Es gibt zwei Arten von Überschreibungen für die Methode "collect".

Methode
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
<R,A> R collect(Collector<? super T,A,R> collector)

Methode durch Containeroperation

Die erste Methode besteht darin, jede Operation durch den Ergebniscontainer selbst zu definieren. Wenn Sie die Operationen zum Erstellen, Hinzufügen und Verknüpfen übergeben, wird jede Operation zur Reduzierung verwendet.

Lieferant ~ generieren ~

Lieferant ist der Prozess des Erstellens einer Instanz des Ergebniscontainers. Grundsätzlich übergeben Sie häufig die result container class :: new. Wenn Sie jedoch eine Factory-Klasse haben, können Sie auch deren Erstellungsmethode übergeben. Bei der Parallelverarbeitung wird der Lieferant mehrmals aufgerufen, jedes Mal muss jedoch eine neue Instanz erstellt werden.

Akku ~ hinzugefügt ~

"Akkumulator" ist der Prozess zum Hinzufügen eines Elements zum Ergebniscontainer. Für Objekte vom Typ Sammlung ist die Methode add anwendbar. Es muss ein Prozess sein, der das Gesetz der Verbindung enthält.

Kombinierer ~ kombinieren ~

"Kombinierer" ist das Verfahren zum Kombinieren von zwei Ergebniscontainern. Für die parallele Verarbeitung werden die in jedem Thread erstellten Ergebnismethoden durch "Kombinierer" kombiniert. Für Objekte vom Typ "Sammlung" gilt die Methode "addAll". Wie bei der normalen Reduktionsverarbeitung muss das Kombinationsgesetz festgelegt werden und die Verarbeitung muss mit dem "Akkumulator" kompatibel sein. Es wird auch nicht für sequentielle Streams verwendet.

Der Reduktionsprozess, bei dem die Elemente im Stream in ArrayList gespeichert und zurückgegeben werden, lautet beispielsweise wie folgt.

Stream.iterate(1, x->x+1)
      .limit(10)
      .parallel()
      .collect(ArrayList::new,
               Collection::add,
               Collection::addAll));

Verwendung von Collcetor

Collector ist ein Objekt, das die Operationen "Lieferant", "Akkumulator" und "Kombinierer" kapselt. Der Collector selbst ist eine Schnittstelle, und zusätzlich zu den obigen drei Operationen werden die "Eigenschaften" -Methode definiert, die den Merkmalssatz der Sammlung zurückgibt, und die "Finisher" -Methode, die den endgültigen Konvertierungsprozess ausführt. Sie können Ihr eigenes Collector-Objekt erstellen, aber die Collctors-Klasse verfügt über viele statische Methoden, die nützliche Collectors zurückgeben.

Zum Beispiel kann der Prozess des Erstellens einer Liste wie zuvor unter Verwendung der Methode "toList" wie folgt beschrieben werden.

Stream.iterate(1, x->x+1)
      .limit(10)
      .parallel()
      .collect(Collectors.toList()));

ToList garantiert jedoch nicht den Typ der zurückgegebenen Liste (obwohl es ArrayList war, als ich es im obigen Prozess ausprobiert habe). Wenn Sie den zu verwendenden Auflistungstyp genauer festlegen möchten, gibt es eine toCollection-Methode.

Stream.iterate(1, x->x+1)
      .limit(10)
      .parallel()
      .collect(Collectors.toCollection(ArrayList::new)));

Bei der Methode "toCollection" wird nur der "Lieferant" (Generierungsprozess) übergeben. Jede Implementierungsklasse von Collection kann verwendet werden, also HashSet (obwohl es auch eine Methode namens "toSet" für Set gibt).

In der Collectors-Klasse gibt es viele praktische Methoden wie diese, sodass Sie die Variablenreduzierung problemlos verwenden können, indem Sie sie verwenden.

Recommended Posts

Informationen zur Java8-Stream-Reduzierung
[Java] Stream API Zwischenoperation
Java 8 studieren (Stream)
Java Stream-Beendigung
[Java] Stream-Verarbeitung
Java 9 Optional :: stream
[Java] Dateisystembetrieb
[Java] Stream Collectors Hinweis
[Java] Stream API-Stream-Generierung
[Java] Stream API / Map
Grobe Zusammenfassung des Java8-Streams
[Java11] Stream-Zusammenfassung - Vorteile von Stream -
Grundlagen der Zeichenoperation (Java)
Java Stream API Spickzettel
Java Stream API in 5 Minuten
Java8-Stream, Zusammenfassung des Lambda-Ausdrucks
[Java] Stream API - Stream-Beendigungsverarbeitung
[Java] Stream API - Stream Zwischenverarbeitung
Java Stream kann nicht wiederverwendet werden.
[Java] Einführung in die Stream-API
[Java11] Stream Usage Summary -Basics-
Java-Anwendung für Anfänger: Stream
[Java 8] Doppelte Löschung (& doppelte Überprüfung) mit Stream
[Java] Stream (Filter, Map, ForEach, Reduce)
[java8] Um die Stream-API zu verstehen
Über Lambda, Stream, LocalDate von Java8
[Einführung in Java] Informationen zur Stream-API
[Java] Elementexistenzprüfung mit Stream
Java
Ich habe versucht, die Java8 Stream API zu verwenden
Grundlegender Verarbeitungsablauf von Java Stream
Java
Java 8 ~ Stream API ~ startet jetzt
Java-Array / Liste / Stream gegenseitige Konvertierungsliste
Java8-Listenkonvertierung mit Stream Map
Verwenden Sie Stream in Java?
Versuchen Sie es mit der Stream-API in Java
Heutzutage Java Lambda Expressions und Stream API
Probieren Sie verschiedene Java Stream API-Methoden aus (jetzt)
[Java] Vergleich von Sammlungs- und StringBuilder-Operationsmethoden
Elementoperationsmethode in Appium TIPS (Java)