Parece que tengo problemas para entender cómo Java compone las operaciones de transmisión en una tubería de transmisión.
Al ejecutar el siguiente código
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());
}
La consola solo imprime 4
. El StringBuilder
objeto todavía tiene el valor ""
.
Cuando agrego la operación de filtro: 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());
}
La salida cambia a:
4
1234
¿Cómo cambia esta operación de filtro aparentemente redundante el comportamiento de la tubería de flujo compuesta?
java
java-stream
atalantus
fuente
fuente
Respuestas:
La
count()
operación del terminal, en mi versión del JDK, termina ejecutando el siguiente código:Si hay una
filter()
operación en la tubería de operaciones, el tamaño de la secuencia, que se conoce inicialmente, ya no se puede conocer (ya quefilter
podría rechazar algunos elementos de la secuencia). Por lo tanto, elif
bloque no se ejecuta, las operaciones intermedias se ejecutan y StringBuilder se modifica así.Por otro lado, si solo tiene
map()
en la tubería, se garantiza que el número de elementos en la secuencia será el mismo que el número inicial de elementos. Entonces, el bloque if se ejecuta, y el tamaño se devuelve directamente sin evaluar las operaciones intermedias.Tenga en cuenta que la lambda pasada
map()
viola el contrato definido en la documentación: se supone que es una operación sin interferencia, sin estado, pero no es sin estado. Por lo tanto, tener un resultado diferente en ambos casos no puede considerarse como un error.fuente
flatMap()
podría cambiar la cantidad de elementos, ¿fue esa la razón por la que inicialmente estaba ansioso (ahora vago)? Entonces, la alternativa sería usarforEach()
y contar por separado simap()
en su forma actual viola el contrato, supongo.4 1234
sin utilizar el filtro adicional o producir efectos secundarios en la operación map ()?int count = array.length; String result = String.join("", array);
Collectors.joining("")
En jdk-9 estaba claramente documentado en java docs
Nota de API:
fuente
Esto no es para lo que sirve .map. Se supone que debe usarse para convertir una secuencia de "Algo" en una secuencia de "Algo más". En este caso, está utilizando el mapa para agregar una cadena a un Stringbuilder externo, después de lo cual tiene una secuencia de "Stringbuilder", cada uno de los cuales fue creado por la operación de mapa agregando un número al Stringbuilder original.
Su flujo en realidad no hace nada con resultados mapeados en el flujo, por lo que es perfectamente razonable suponer que el procesador de flujo puede omitir el paso. Estás contando con efectos secundarios para hacer el trabajo, lo que rompe el modelo funcional del mapa. Sería mejor servirte usando forEach para hacer esto. Haga el recuento como una secuencia separada por completo, o coloque un contador usando AtomicInt en forEach.
El filtro lo obliga a ejecutar el contenido del flujo ya que ahora tiene que hacer algo nocionalmente significativo con cada elemento del flujo.
fuente