Es scheint, dass ich Probleme habe zu verstehen, wie Java Stream-Operationen zu einer Stream-Pipeline zusammensetzt.
Bei der Ausführung des folgenden Codes
public
static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
Die Konsole druckt nur 4
. Das StringBuilder
Objekt hat noch den Wert ""
.
Wenn ich die Filteroperation hinzufüge: filter(s -> true)
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.filter(s -> true)
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
Die Ausgabe ändert sich zu:
4
1234
Wie ändert diese scheinbar redundante Filteroperation das Verhalten der zusammengesetzten Stream-Pipeline?
java
java-stream
Atalantus
quelle
quelle
Antworten:
Die
count()
Terminaloperation führt in meiner Version des JDK den folgenden Code aus:Wenn sich eine
filter()
Operation in der Pipeline von Operationen befindet, kann die Größe des Streams, die anfangs bekannt ist, nicht mehr bekannt sein (dafilter
einige Elemente des Streams abgelehnt werden könnten). Derif
Block wird also nicht ausgeführt, die Zwischenoperationen werden ausgeführt und der StringBuilder wird somit geändert.Wenn Sie jedoch nur
map()
in der Pipeline sind, entspricht die Anzahl der Elemente im Stream garantiert der ursprünglichen Anzahl der Elemente. Der if-Block wird also ausgeführt und die Größe wird direkt zurückgegeben, ohne die Zwischenoperationen auszuwerten.Beachten Sie, dass das übergebene Lambda
map()
gegen den in der Dokumentation definierten Vertrag verstößt: Es soll sich um eine nicht störende, zustandslose Operation handeln, die jedoch nicht zustandslos ist. Ein unterschiedliches Ergebnis in beiden Fällen kann daher nicht als Fehler angesehen werden.quelle
flatMap()
War dies der Grund, warum es anfangs eifrig (jetzt faul) war, weil es möglicherweise in der Lage war, die Anzahl der Elemente zu ändern? Die Alternative wäre also,forEach()
separat zu verwenden und zu zählen, wennmap()
in der aktuellen Form der Vertrag verletzt wird, denke ich.4 1234
ohne den zusätzlichen Filter zu verwenden oder Nebenwirkungen in der map () -Operation zu erzeugen?int count = array.length; String result = String.join("", array);
Collectors.joining("")
In jdk-9 wurde es in Java-Dokumenten klar dokumentiert
API-Hinweis:
quelle
Dafür ist .map nicht gedacht. Es soll verwendet werden, um einen Stream von "Something" in einen Stream von "Something Else" zu verwandeln. In diesem Fall verwenden Sie map, um eine Zeichenfolge an einen externen Stringbuilder anzuhängen. Anschließend haben Sie einen Stream von "Stringbuilder", der jeweils durch die Map-Operation erstellt wurde, indem eine Nummer an den ursprünglichen Stringbuilder angehängt wird.
Ihr Stream macht eigentlich nichts mit zugeordneten Ergebnissen im Stream, daher ist es durchaus vernünftig anzunehmen, dass der Schritt vom Stream-Prozessor übersprungen werden kann. Sie rechnen bei der Arbeit mit Nebenwirkungen, die das Funktionsmodell der Karte beschädigen. Sie sollten besser mit forEach bedient werden, um dies zu tun. Führen Sie die Zählung vollständig als separaten Stream durch oder setzen Sie einen Zähler mit AtomicInt in forEach.
Der Filter zwingt ihn, den Stream-Inhalt auszuführen, da er nun mit jedem Stream-Element etwas fiktiv Bedeutendes tun muss.
quelle