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.
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).
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
"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.
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).
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);
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) |
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
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.
"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" 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));
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