Cuando Java introdujo Streams en Java 8, no fue solo para “hacer código más lindo”, sino para cambiar la forma en que procesamos colecciones.
Pero otros lenguajes de la JVM ya resolvían esto de forma distinta.
Entonces, la pregunta es: ¿por qué Java eligió Streams y no otro modelo?
Antes de Java 8:
List<String> result = new ArrayList<>();
for (String s : lista) {
if (s.length() > 3) {
result.add(s.toUpperCase());
}
}
Mucho boilerplate
- No es declarativo
- Difícil de paralelizar
- Mezcla lógica con control de flujo
La solución: Streams en Java
Con Streams:
List<String> result = lista.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.toList();
Claves del diseño:
- Evaluación lazy
- Pipeline de operaciones
- Separación entre datos y procesamiento
- Fácil paralelización (parallelStream())
Java construyó un modelo nuevo, no solo métodos en Collection ¿y por qué? Otros lenguajes los hacían bien.
Scala
lista.filter(_.length > 3).map(_.toUpperCase)
Características
- Colecciones inmutables por defecto
- Operaciones directamente sobre la colección
- Lazy solo si usás View o LazyList
Scala no necesita Streams porque su API de colecciones ya es funcional
Kotlin
lista.filter { it.length > 3 }
.map { it.uppercase() }
Características:
- API funcional sobre colecciones
- Operaciones eager por defecto
Para lazy:
lista.asSequence()
.filter { it.length > 3 }
.map { it.uppercase() }
Diferencia clave
Kotlin separa:
- List (eager)
- Sequence (lazy)
Java unificó esto en Streams.
Groovy
lista.findAll { it.length() > 3 }
.collect { it.toUpperCase() }
Características
- Muy expresivo
- Dinámico
- API funcional desde hace años
Diferencia clave
Más simple, pero:
- menos eficiente
- no lazy por defecto
- sin optimización tipo pipeline
Entonces ¿Por qué Java eligió Streams?
Java tenía restricciones fuertes:
1. Compatibilidad hacia atrás, no podía romper Collection
2. Necesidad de lazy evaluation para evitar:
- listas intermedias
- consumo extra de memoria
3. Paralelismo
Streams permiten: lista.parallelStream() sin cambiar el código lógico
4. Pipeline optimizable
La JVM puede optimizar: filter → map → reduce, como una sola operación
Java no eligió Streams por casualidad.
Fue una decisión para:
- mantener compatibilidad
- introducir programación funcional
- mejorar performance
- habilitar paralelismo
Mientras otros lenguajes:
- ya eran funcionales (Scala)
- o tomaron caminos más simples (Kotlin, Groovy)
