web-dev-qa-db-de.com

Berücksichtigen von Listenelementen, die nach der Erstellung eines gefilterten Streams hinzugefügt werden

Gegeben der folgende Code:

List<String> strList = new ArrayList<>(Arrays.asList("Java","Python","Php"));

Stream<String> jFilter = strList.stream().filter(str -> str.startsWith("J"));

strList.add("JavaScript"); // element added after filter creation
strList.add("JQuery"); // element added after filter creation

System.out.println(Arrays.toString(jFilter.toArray())); 

welche Ausgänge:

[Java, JavaScript, JQuery]

Warum erscheinen JavaScript und JQuery im gefilterten Ergebnis, obwohl sie nach dem Erstellen des gefilterten Streams hinzugefügt wurden?

20
mmuzahid

Kurze Antwort 

Sie nehmen nach diesem Punkt an:

Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));

Dass ein neuer Strom der Elemente, die mit "J" beginnen, zurückgegeben werden, d. H. Nur Java. Dies ist jedoch nicht der Fall; 

streams sind lazy, d. h. sie keine Logik ausführen, sofern nicht durch eine Terminaloperation etwas anderes gesagt wird.

Die eigentliche Ausführung der Stream-Pipeline beginnt mit dem Aufruf toArray(). Da die Liste vor dem Ende der toArray()-Operation des Terminals geändert wurde, lautet das Ergebnis [Java, JavaScript, JQuery].

Längere Antwort 

hier ist ein Teil der Dokumentation die dies erwähnt:

Bei Stream-Quellen mit gutem Verhalten kann die Quelle vor .__ geändert werden. der Terminalbetrieb beginnt und diese Änderungen werden spiegelt sich in den abgedeckten Elementen. Betrachten Sie zum Beispiel Folgendes Code:

 List<String> l = new ArrayList(Arrays.asList("one", "two"));
 Stream<String> sl = l.stream();
 l.add("three");
 String s = sl.collect(joining(" "));  

Zuerst wird eine Liste erstellt, die aus zwei Zeichenfolgen besteht: "Eins"; und zwei". Dann wird ein Stream erstellt von dieser Liste. Als Nächstes wird die Liste durch Hinzufügen einer dritten Zeichenfolge geändert: "drei". Zum Schluss werden die Elemente des Streams gesammelt und verbunden zusammen. Da die Liste vor der Terminalsammlung geändert wurde Die Operation wurde gestartet. Das Ergebnis ist eine Zeichenfolge "eins zwei drei" . Alle von JDK-Sammlungen zurückgegebenen Streams und die meisten anderen JDK-Dateien Klassen werden auf diese Weise benommen;

13
Aomine

Bis zur Aussage 

System.out.println(Arrays.toString(jFilter.toArray()));

läuft, der Stream macht nichts. Eine Terminaloperation (im Beispiel toArray) ist erforderlich, damit der Stream durchlaufen und Ihre Zwischenoperationen (in diesem Fall filter) ausgeführt werden können.

In diesem Fall können Sie beispielsweise die Größe der Liste erfassen, bevor Sie weitere Elemente hinzufügen:

int maxSize = strList.size();
Stream<String> jFilter = strStream.limit(maxSize)
                                  .filter(str -> str.startsWith("J"));

dabei lässt limit(maxSize) nicht mehr als die ursprünglichen Elemente durch die Pipeline.

8
ernest_k

Das liegt daran, dass der Stream nie ausgewertet wurde. Sie haben nie eine " Terminaloperation " in diesem Stream aufgerufen, damit sie ausgeführt wird, da sie Lazy sind.

Sehen Sie sich eine Änderung Ihres Codes und der Ausgabe an. Die Filterung findet tatsächlich statt, wenn Sie den Terminal Operator anrufen.

 public static void main(String []args){
         List<String> strList = new ArrayList<>();
    strList.add("Java");
    strList.add("Python");
    strList.add("Php");

    Stream<String> strStream = strList.stream();

    Stream<String> jFilter = strStream.filter(str -> {
        System.out.println("Filtering" + str);
        return str.startsWith("J");
        });

 System.out.println("After Stream creation");
    strList.add("JavaScript"); // element added after filter creation
    strList.add("JQuery"); // element added after filter creation

    System.out.println(Arrays.toString(jFilter.toArray()));

     }

Ausgabe:

After Stream creation
FilteringJava
FilteringPython
FilteringPhp
FilteringJavaScript
FilteringJQuery
[Java, JavaScript, JQuery]
3
Bandi Kishore

Wie in der offiziellen Dokumentation unter, https://docs.Oracle.com/javase/8/docs/api/Java/util/stream/package-summary.html erläutert, verfügen Streams über keinen Speicher eher wie Iteratoren als Sammlungen und werden faul bewertet.

In Bezug auf den Stream passiert also wirklich nichts, bis Sie die Terminaloperation toArray () aufrufen.

1
GreyBeardedGeek

@Hadi Js Kommentar, aber es sollte laut den Regeln beantwortet werden.

Weil streams faul ist und beim Aufruf der Terminaloperation ausgeführt wurde.

1
user5377037

Die toArray-Methode ist die Terminaloperation und funktioniert mit dem gesamten Inhalt Ihrer Liste. Um ein vorhersagbares Ergebnis zu erhalten, speichern Sie stream nicht in einer temporären Variablen, da dies zu irreführenden Ergebnissen führen kann. Ein besserer Code ist:

String[] arr = strList.stream().filter(str -> str.startsWith("J")).toArray();
0
fastcodejava