В Kotlin Sequence — это ленивые последовательности, которые позволяют работать с большими или даже потенциально бесконечными наборами данных эффективно. Операции над Sequence (например, map, filter) не выполняются немедленно; вместо этого строится цепочка действий, и вычисление происходит только при необходимости (при терминальной операции, например, toList).
Почему это важно:
Пример кода:
val numbers = generateSequence(1) { it + 1 } .filter { it % 2 == 0 } .map { it * it } .take(5) .toList() println(numbers) // [4, 16, 36, 64, 100]
В этом примере только 5 первых элементов последовательности будут реально вычислены, несмотря на то, что она потенциально бесконечна.
Когда использовать Sequence:
Можно ли гарантировать, что операторы типа
mapиfilterв обычных коллекциях Kotlin также ленивые? Почему?
Ответ: Нет, стандартные коллекционные операции (List.map, List.filter и т.д.) в Kotlin — строго жадные. Каждая операция создаёт промежуточную коллекцию и немедленно обрабатывает все элементы. Ленивость поддерживается только при использовании Sequence.
Пример:
val data = listOf(1,2,3,4) val mapped = data.map { println("Mapping $it"); it * 2 } // Сразу печатает все значения
История
В проекте, обрабатывающем большие CSV-файлы (до гигабайта на запись), коллекция сначала загружалась полностью в List, после чего применялись chain map/filter. Приложение «ложилось» по OutOfMemory — проблема была решена заменой List на Sequence, чей map/filter не создавал огромных промежуточных списков.
История
Backend-сервис для агрегации отчетов из множества БД сначала накапливал результаты поиска в List и потом фильтровал: запросы стали медленными, наблюдались лаги GC. Замена на генератор + Sequence позволила агрегировать данные на лету, уменьшив задержки в разы.
История
Мигрированный с Java проект использовал стандартные расширения Kotlin (map, filter) без перехода на Sequence при работе с большими потоками данных, полагая, что код работает лениво, как стримы Java 8. Ошибка приводила к серьёзным утечкам памяти и внезапным сбоям в проде.