En Kotlin, Sequence son secuencias perezosas que permiten trabajar de manera eficiente con grandes o incluso potencialmente infinitos conjuntos de datos. Las operaciones sobre Sequence (como map, filter) no se ejecutan de inmediato; en lugar de eso, se construye una cadena de acciones y el cálculo solo ocurre cuando es necesario (en una operación terminal, como toList).
¿Por qué es importante esto?
Ejemplo de código:
val numbers = generateSequence(1) { it + 1 } .filter { it % 2 == 0 } .map { it * it } .take(5) .toList() println(numbers) // [4, 16, 36, 64, 100]
En este ejemplo, solo se calcularán realmente los 5 primeros elementos de la secuencia, a pesar de que es potencialmente infinita.
Cuándo usar Sequence:
¿Se puede garantizar que los operadores como
mapyfilteren colecciones normales de Kotlin también sean perezosos? ¿Por qué?
Respuesta: No, las operaciones de colecciones estándar (List.map, List.filter, etc.) en Kotlin son estrictamente codiciosas. Cada operación crea una colección intermedia y procesa inmediatamente todos los elementos. La pereza solo se mantiene al usar Sequence.
Ejemplo:
val data = listOf(1,2,3,4) val mapped = data.map { println("Mapping $it"); it * 2 } // Imprime todos los valores inmediatamente
Historia
En un proyecto que procesaba grandes archivos CSV (de hasta un gigabyte al escribir), la colección se cargaba primero completamente en una List, después se aplicaban cadenas de map/filter. La aplicación se "caía" por OutOfMemory; el problema se resolvió reemplazando List por Sequence, cuyo map/filter no creaba enormes listas intermedias.
Historia
Un servicio de backend para agregar reportes de múltiples bases de datos primero acumulaba los resultados de búsqueda en una List y luego filtraba: las consultas se volvieron lentas, se observaron retrasos en el GC. Reemplazar con un generador + Sequence permitió agregar datos sobre la marcha, reduciendo las latencias drásticamente.
Historia
Un proyecto migrado desde Java utilizaba las extensiones estándar de Kotlin (map, filter) sin pasar a Sequence al trabajar con grandes flujos de datos, suponiendo que el código funcionaba de manera perezosa, como los streams de Java 8. El error causó serias fugas de memoria y fallos repentinos en producción.