Zuordnungen für Sammlungsklassen, die funktionale Operationen für einen Strom von Elementen unterstützen, z. B. Transformationen reduzieren. Quelle: Offizielles Dokument
Teilweise geändert von Official Reference
stream.java
final int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
Im obigen Beispiel wird "Widgets" aus "Widgets" mit "Farbe" "ROT" extrahiert, um den Gesamtwert von "Gewicht" zu berechnen. Wenn Sie es als Anweisung eingeben, lautet es wie folgt.
zur Aussage.java
int sum = 0;
for (Widget w: widgets) {
if (w.getColor() == RED) {
sum += w.getWeight();
}
}
Wie ich am Anfang schrieb, ist Stream "* eine Klasse, die funktionale Operationen für einen Stream von Elementen unterstützt, z. B. die Konvertierung von Kartenreduzierungen für Sammlungen. *"
Grob gesagt ist es das, was ich mit der for-Anweisung gemacht habe. Sie können den oben genannten Gesamtwert berechnen, den Maximalwert ermitteln, eine neue Liste erstellen usw.
Viele der Stream-Klassenmethoden verwenden eine funktionale Schnittstelle als Argument. Übergeben Sie beispielsweise ** das Filtern ** an die Filterfunktion und ** das Konvertieren in den Integer-Typ ** an die mapToInt-Funktion. Stream verfügt über viele Methoden, die einen neuen Stream zurückgeben, und Sie können die Verarbeitung zur flexiblen Verarbeitung an diesen übergeben.
Ich denke, es gibt zwei Hauptvorteile:
Dies liegt daran, dass die Schreibmethode mit Strean im Vergleich zu normalem Code ** "deklarativ" ** ist. Achten Sie besonders auf den Teil des obigen Beispiels, in dem die Summe ("Summe") berechnet wird.
Für Stream
Einfach sum ()
aufrufen.
Ich sage nur ** "finde den Gesamtwert" ** für den Datensatz.
Für Aussage Der Anfangswert von "sum" wird auf 0 gesetzt und die Werte jedes Elements werden addiert. Der Endwert ist der Gesamtwert. ** "So finden Sie den Gesamtwert" ** ist implementiert.
Die for-Anweisung heißt im Vergleich zum Stream ** "instruktiv" **. Durch "deklaratives" Schreiben können Sie kleine Implementierungen ausblenden und die Codemenge reduzieren. Es ist nur eine Erklärung, was zu tun ist, sodass Sie anhand des Codes leicht verstehen können, was Sie tun möchten. Andererseits ist es umso schwieriger zu verstehen, je komplizierter der Prozess ist und je mehr Fehler verursacht werden.
Erfahrungsgemäß ist es einfacher, Code zu lesen und sicherer zu machen, wenn eine Methode weniger wichtigen Code enthält, unabhängig davon, ob Stream verwendet wird oder nicht.
Es gibt viele Fälle, in denen dieser Vorteil nicht erzielt werden kann. (Erfahrung) Dies liegt daran, dass die Ausführung in Serie ** schneller ** ist, es sei denn, Sie haben es mit einer sehr großen Datenmenge zu tun.
Glücklicherweise müssen Sie nur "parallel ()" aufrufen, um die Ausführung von Strean in eine parallele Verarbeitung umzuwandeln. Sie können die Vorteile der Parallelverarbeitung maximieren, indem Sie ** die Datenmenge ** und ** das Problem berücksichtigen, die Reihenfolge nicht beizubehalten **, anstatt die Parallelverarbeitung in den dunklen Wolken durchzuführen.
Die folgenden Artikel sind hilfreich. Referenz: Grundkenntnisse der Funktionsschnittstelle und der Stream-API, die ihren wahren Charakter im Lambda-Ausdruck (3/3) zeigen
Es gibt ** 3 Schritte **, um einen Prozess mit Stream zu schreiben.
Ein Beispiel ist wie folgt.
stream.java
final int sum = widgets.stream() //Stream erstellen
.filter(w -> w.getColor() == RED) //Zwischenbetrieb
.mapToInt(w -> w.getWeight()) //Zwischenbetrieb
.sum(); //Kündigungsoperation
Ich denke, dass es oft aus einer Sammlung wie List oder Set erstellt wird. Genau das ist "widgets.stream ()". Darüber hinaus gibt es Methoden wie das Erstellen eines Streams aus mehreren Werten und die Verwendung von Stream.Builder.
filter`` mapToInt
ist die Zwischenoperation. Es konvertiert den Wert jedes Elements und extrahiert das Element basierend auf der Bedingung.
Gibt einen Stream wie "Stream
public class Main {
public static void main(String[] args) throws Exception {
List<Widget> widgets = List.of(new Widget(RED, 10), new Widget(BLUE, 20));
Stream<Widget> stream1 = widgets.stream();
Stream<Widget> stream2 = stream1.filter(w -> w.getColor() == RED);
System.out.println("complete filtering");
IntStream stream3 = stream2.mapToInt(w -> w.getWeight());
System.out.println("complete mappint to integer");
final int sum = stream3.sum();
}
}
class Widget {
private Color color;
private int weight;
public Widget(Color color, int weight) {
this.color = color;
this.weight = weight;
}
public Color getColor() {
System.out.println(color);
return color;
}
public int getWeight() {
System.out.println(weight);
return weight;
}
}
complete filtering
complete mappint to integer
java.awt.Color[r=255,g=0,b=0]
10
java.awt.Color[r=0,g=0,b=255]
Es ist ersichtlich, dass nicht jeder Prozess unmittelbar nach der Zwischenoperation ausgeführt wird.
sum
ist die Beendigungsoperation. Andere schließen collect`` findFirst
ein. Gibt den Gesamtwert oder eine neue Sammlung zurück.
Im Gegensatz zur Zwischenoperation variiert der zurückgegebene Wert. Es gibt viele Dinge, die Sie tun können. "Gesamtwert berechnen", "Neue Sammlung erstellen", "Für jedes Element (für jedes Element) verarbeiten" usw.
Ich denke, es ist besser, ein Beispiel dafür zu zeigen, was für Zwischenoperationen und Terminierungsoperationen getan werden kann, daher möchte ich ab dem nächsten Mal detailliert zusammenfassen.
Ich habe es bisher ignoriert, aber ich möchte die Existenz von w-> w.getColor () == RED`` w-> w.getWeight ()
erwähnen.
Diese Grammatik heißt ** Lambda-Ausdruck **, aber Sie müssen nicht unbedingt einen Lambda-Ausdruck schreiben.
** Lambda-Ausdrücke sind nur eine Möglichkeit, eine funktionale Schnittstelle zu implementieren. ** ** **
Dieses Verständnis ist für das Verständnis von Stream unvermeidlich, da die meisten Methoden in Stream eine funktionale Schnittstelle als Argument verwenden.
Um ehrlich zu sein, ist es in Ordnung, zunächst in einer Atmosphäre zu schreiben. Selbst wenn Sie von nun an das Schlimmste nicht verstehen können, ist es möglicherweise in Ordnung, einen Stream zu schreiben.
Eine Art Schnittstelle. Diese Schnittstelle hat ** nur eine Methode ** Was in Java standardmäßig bereitgestellt wird , Sie können auch Ihre eigenen erstellen.
Um jeden Typ leicht verständlich zu machen, ist das erste Beispiel so weit wie möglich unterteilt.
Stream<Widget> stream1 = widgets.stream();
Predicate<Widget> predicate = w -> w.getColor() == RED;
Stream<Widget> stream2 = stream1.filter(predicate);
ToIntFunction<Widget> toIntFunction = w -> w.getWeight();
IntStream stream3 = stream2.mapToInt(toIntFunction);
final int sum = stream3.sum();
Das Predicate`` ToIntFunction
, dem der Lambda-Ausdruck zugewiesen ist, ist die funktionale Schnittstelle. Die Definition von "ToIntFunction" lautet wie folgt.
java:java.util.function.ToIntFunction.java
@FunctionalInterface
public interface ToIntFunction<T> {
int applyAsInt(T value);
}
@ FunctionalInterface
wird zur Funktionsschnittstelle hinzugefügt, es gibt jedoch kein Funktionsproblem, auch wenn es nicht hinzugefügt wird.
Sie müssen eine Instanz erstellen, die eine funktionale Schnittstelle implementiert, um sie an eine Stream-Methode zu übergeben. Es gibt jedoch drei Hauptmethoden, um sie zu implementieren.
ist.
Vergleichen wir jede Implementierungsmethode. Als Voraussetzung gehen wir davon aus, dass Sie eine solche Widget-Klasse haben.
Widget.java
class Widget {
private Color color;
private int weight;
public Widget(Color color, int weight) {
this.color = color;
this.weight = weight;
}
public Color getColor() {
return color;
}
public boolean isColorRed() {
return color == RED;
}
public int getWeight() {
return weight;
}
}
Es ist nicht sehr leicht zu lesen.
Anonyme Klasse.java
final int sum = widgets.stream()
.filter(new Predicate<Widget>() {
public boolean test(Widget w) {
return w.isColorRed();
}
})
.mapToInt(new ToIntFunction<Widget>() {
public int applyAsInt(Widget w) {
return w.getWeight();
}
})
.sum();
Das funktioniert natürlich immer noch.
static class WidgetTestColorIsRed implements Predicate<Widget> {
public boolean test(Widget w) {
return w.isColorRed();
}
}
static class WidgetToWeightFunction implements ToIntFunction<Widget> {
public int applyAsInt(Widget w) {
return w.getWeight();
}
}
final int sum = widgets.stream()
.filter(new WidgetTestColorIsRed())
.mapToInt(new WidgetToWeightFunction())
.sum();
Ich glaube nicht, dass es viele Fälle gibt, in denen man einfach schreiben kann.
Es ist überwiegend leichter zu lesen.
Lambda-Stil.java
final int sum = widgets.stream()
.filter(w -> w.isColorRed())
.mapToInt(w -> w.getWeight())
.sum();
Es gibt verschiedene Möglichkeiten, einen Lambda-Stil zu schreiben.
//Keine Argumente
() -> "Konstante";
//1 Argument
n -> n + 1; //Klammern können weggelassen werden
(n) -> 2 * n;
//2 oder mehr Argumente
(a, b) -> Math.sqrt(a * a + b * b);
(x, y, z) -> x * y * z;
//Mehrere Zeilen
(a, b, c) -> {
double s = (a + b + c) / 2;
return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
Ich habe es bisher noch nicht verwendet, aber wenn Sie es mit einer Methodenreferenz schreiben können, ist es einfacher, den Code zu sehen, wenn Sie ihn positiv verwenden **. Es ist leicht zu lesen, da Sie keine Variable wie "w" eingeben müssen und wissen, was der Stream-Elementtyp zu diesem Zeitpunkt ist.
Schreiben Sie als class :: method
.
Methodenreferenz.java
final int sum = widgets.stream()
.filter(Widget::isColorRed)
.mapToInt(Widget::getWeight)
.sum();
Anonyme Klassen, Lambda-Ausdrücke und Methodenreferenzen sind alle Möglichkeiten, eine funktionale Schnittstelle zu instanziieren. Sie können eine Instanz auf die gleiche Weise erstellen, auch wenn Sie über eine von Ihnen selbst definierte Funktionsschnittstelle verfügen. In einer funktionalen Schnittstelle wissen wir, dass wir nur eine Methode implementieren müssen, sodass auch ein Schreibstil ohne Typinformationen, wie z. B. ein Lambda-Ausdruck, abgeleitet werden kann.
Das nächste Mal möchte ich die spezifische Verwendung von Stream vorstellen.
Recommended Posts