java - instream - Teniendo en cuenta los elementos de lista que se agregan después de la creación de secuencias filtradas
stream parallel java 8 (6)
Respuesta corta
Estás asumiendo después de este punto:
Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));
Que se devuelva una nueva secuencia de los elementos que comienzan con "J", es decir, solo Java
. Sin embargo, este no es el caso;
los flujos son perezosos, es decir, no realizan ninguna lógica a menos que se indique lo contrario por una operación de terminal.
La ejecución real de la toArray()
del flujo comienza en la llamada toArray()
y como la lista se modificó antes de que la toArray()
terminal toArray()
comenzara, el resultado será [Java, JavaScript, JQuery]
.
Respuesta más larga
Aquí hay una parte de la documentation que menciona esto:
Para fuentes de flujo de buen comportamiento, la fuente se puede modificar antes de que comience la operación del terminal y esas modificaciones se reflejarán en los elementos cubiertos. Por ejemplo, considere el siguiente código:
List<String> l = new ArrayList(Arrays.asList("one", "two")); Stream<String> sl = l.stream(); l.add("three"); String s = sl.collect(joining(" "));
Primero se crea una lista que consta de dos cadenas: "uno"; y dos". Entonces se crea una secuencia de esa lista. A continuación, la lista se modifica agregando una tercera cadena: "tres". Finalmente los elementos de la corriente se recogen y se unen. Dado que la lista se modificó antes de que comenzara la operación de recolección del terminal, el resultado será una cadena de "uno dos tres". Todas las secuencias devueltas de las colecciones JDK, y la mayoría de las otras clases de JDK, se comportan de esta manera;
Dado el siguiente código:
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()));
que produce:
[Java, JavaScript, JQuery]
¿Por qué aparecen JavaScript
y JQuery
en el resultado filtrado aunque se agregaron después de crear el flujo filtrado?
Como se explica en la documentación oficial en documentation , los flujos no tienen almacenamiento, y por lo tanto son más parecidos a iteradores que Las colecciones, y son evaluadas perezosamente.
Entonces, nada sucede realmente con respecto al flujo hasta que invoque la operación del terminal toArray ()
El comentario de @Hadi J, pero debería ser respondido de acuerdo a las reglas.
Debido a que los
streams
son perezosos y cuando se llama a la operación de terminal, se ejecuta.
El método toArray
es la operación del terminal y funciona en ese contenido completo de su lista. Para obtener un resultado predecible, no guarde la stream
en una variable temporal, ya que dará lugar a resultados confusos. Un mejor código es:
String[] arr = strList.stream().filter(str -> str.startsWith("J")).toArray();
Es porque la corriente nunca fue evaluada. nunca llamó a una " Operación de terminal " en ese flujo para que se ejecute ya que son perezosos .
Mira una modificación de tu código y la salida. El filtrado en realidad tiene lugar cuando llama al operador de terminal.
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()));
}
Salida:
After Stream creation
FilteringJava
FilteringPython
FilteringPhp
FilteringJavaScript
FilteringJQuery
[Java, JavaScript, JQuery]
Hasta la declaración
System.out.println(Arrays.toString(jFilter.toArray()));
corre, la corriente no hace nada. Se toArray
una operación de terminal ( toArray
en el ejemplo) para que la corriente sea atravesada y sus operaciones intermedias ( filter
en este caso) para ser ejecutadas.
En este caso, lo que puede hacer es, por ejemplo, capturar el tamaño de la lista antes de agregar otros elementos:
int maxSize = strList.size();
Stream<String> jFilter = strStream.limit(maxSize)
.filter(str -> str.startsWith("J"));
donde limit(maxSize)
no permitirá que más de los elementos iniciales pasen por la tubería.