Cómo dejar de escribir Stream.collect en Kotlin y empezar a vivir
¿Alguna vez has migrado de Java a Kotlin y te has sentido como un pez fuera del agua? Especialmente cuando se trataba de streams. En Java 8 estábamos acostumbrados a las cadenas .stream().filter().map().collect(). Y en Kotlin — ¡pum! — no hay Stream.collect en la forma habitual. Pero no te apresures a entrar en pánico. En realidad, en la biblioteca estándar de Kotlin está todo lo mismo, solo que más cómodo, más conciso y sin ruido innecesario.
En este artículo, analizaremos qué equivalentes de Java 8 Stream.collect existen en Kotlin y lo mostraremos con ejemplos reales. Si buscas una respuesta a la pregunta "What Java 8 Stream.collect equivalents are available in the standard Kotlin library?" — estás en el lugar correcto. Repasaremos las operaciones más populares: desde la simple recolección en una lista hasta la agrupación compleja y la unión de cadenas.
¿Listo? ¡Vamos allá!
1. El caso más simple: toList() y toSet()
En Java, para recoger el resultado de un stream en una lista, escribes:
List<String> result = list.stream() .filter(s -> s.length() > 3) .collect(Collectors.toList());En Kotlin esto se convierte en una sola línea sin collect:
val result = list.filter { it.length > 3 }Sí, no has oído mal. filter en Kotlin ya devuelve una lista. Pero si necesitas especificar explícitamente el tipo de colección, usa toList(), toSet() o toMutableList():
val numbers = listOf(1, 2, 3, 4, 5, 5)val uniqueNumbers: Set<Int> = numbers.toSet()// Resultado: [1, 2, 3, 4, 5]Conclusión: en Kotlin, la mayoría de las operaciones con colecciones (filter, map, flatMap) devuelven inmediatamente una lista. Ya no necesitas collect(Collectors.toList()).
2. Agrupación: groupingBy en lugar de Collectors.groupingBy
La agrupación es una de las tareas más comunes. En Java se ve así:
Map<Integer, List<String>> byLength = list.stream() .collect(Collectors.groupingBy(String::length));En Kotlin existe la elegante función groupBy para esto:
val words = listOf("gato", "código", "gato grande", "gatito")val byLength = words.groupBy { it.length }// Resultado: {4=[gato], 6=[código], 11=[gato grande], 6=[gatito]}Pero hay un matiz: groupBy devuelve directamente Map<K, List<V>>. ¿Y si necesitas algo más complejo, como contar el número de elementos en cada grupo? En Java usarías Collectors.counting():
Map<Integer, Long> countByLength = list.stream() .collect(Collectors.groupingBy(String::length, Collectors.counting()));En Kotlin para esto existe groupingBy + eachCount:
val countByLength = words.groupingBy { it.length }.eachCount()// Resultado: {4=1, 6=2, 11=1}¿Y si necesitas recoger no una lista, sino, por ejemplo, un conjunto? Usa groupBy con transformación:
val byLengthSet = words.groupBy(keySelector = { it.length }, valueTransform = { it.uppercase() })// Resultado: {4=[GATO], 6=[CÓDIGO, GATITO], 11=[GATO GRANDE]}Consejo: si necesitas algo intermedio entre groupBy y groupingBy, usa groupingBy — es perezoso y permite aplicar diferentes agregaciones (eachCount, fold, reduce).
3. Unión de cadenas: joinToString() en lugar de Collectors.joining()
En Java, unir cadenas con un separador:
String joined = list.stream() .collect(Collectors.joining(", "));En Kotlin esto se hace aún más simple:
val words = listOf("Kotlin", "Java", "Python")val joined = words.joinToString(", ")