Java 53: Stream Pipelines — map, filter & collect
easy⏱ 5 mincoursejava
Operaciones intermedias vs terminales
Las ops intermedias (map, filter, flatMap, sorted, distinct, limit) son lazy — devuelven un nuevo Stream y añaden una etapa al pipeline pero no ejecutan nada. Las ops terminales (collect, reduce, count, forEach, findFirst) son eager — empujan los elementos por cada etapa y producen un resultado o efecto. Un stream se consume una vez: tras una op terminal queda cerrado.
List<String> names = List.of("ada", "linus", "grace", "ken");
List<String> result = names.stream()
.filter(n -> n.length() > 3) // intermediate (lazy)
.map(String::toUpperCase) // intermediate (lazy)
.sorted() // intermediate (lazy)
.collect(Collectors.toList()); // terminal -> runs the pipeline
// [ADA? no — len 3 dropped] -> [GRACE, LINUS]
Elemento por elemento, no etapa por etapa
Los streams no materializan una lista intermedia entre cada operación. En cambio cada elemento se empuja por todo el pipeline antes de empezar el siguiente. Así filter luego map sobre [1,2,3,4] ejecuta filter(1) → (descartado), filter(2) → map(2), … — esta fusión es por qué los streams usan poca memoria y por qué ops de corto-circuito como findFirst paran temprano.
Construye un stream lazy que pruebe la pereza
Implementa LazyStream<T> con map, filter (cada uno registra una etapa y devuelve un nuevo stream — sin ejecutar nada) y un collect() terminal que finalmente empuja cada elemento fuente por todas las etapas. Lleva un contador mapCalls. Pasa [1,2,3,4,5,6] por filter(par) luego map(x*10) y collect. Imprime el resultado [20,40,60] y confirma que map corrió solo 3 veces (una por elemento sobreviviente), no 6.
Los Collectors son el poder real
collect(Collectors.toList()) es solo el comienzo. groupingBy, partitioningBy, toMap, joining, counting y summingInt convierten un stream en mapas, particiones y agregados en una sola llamada terminal. Recurrir a un for manual para construir un Map suele ser señal de que un Collector se leería mejor.
Quiz: ¿corre filter antes de la op terminal?
Si construyes stream.filter(...).map(...) pero nunca llamas a una op terminal, ¿cuántas veces corre filter? Cero — las ops intermedias son lazy y solo se ejecutan cuando una op terminal jala elementos. Un pipeline sin op terminal no hace nada; es solo una descripción.