Отложенные операции
Избегайте промежуточных списков
«Отложенные операции» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Ленивые операции
Сила последовательностей заключается в ленивых операциях. Промежуточные операции строят конвейер, не запуская его, а элементы через него пропускает только завершающая операция. Это позволяет избежать промежуточных списков.
Без промежуточных списков
При работе с коллекциями каждый вызов map и filter выделяет новый список. В последовательностях элементы проходят через все этапы без создания таких временных списков.
fun main() {
val result = (1..6).asSequence()
.map { it * it }
.filter { it % 2 == 0 }
.toList()
println(result)
}Наблюдаем ленивое выполнение
Без завершающей операции ни одна лямбда не выполняется. Конвейер остаётся лишь рецептом до тех пор, пока его не обработают.
fun main() {
val pipeline = (1..3).asSequence()
.map { println("map $it"); it }
println("Built pipeline, nothing ran")
pipeline.toList()
}take ограничивает обработку
take(n) — ленивая промежуточная операция. В сочетании с бесконечным генератором она останавливается после создания n элементов.
fun main() {
val firstThree = generateSequence(1) { it + 1 }
.map { it * 10 }
.take(3)
.toList()
println(firstThree)
}first прекращает обработку досрочно
Завершающие операции, такие как first, останавливаются сразу после получения нужного результата и полностью пропускают остальные элементы.
fun main() {
val result = (1..1000).asSequence()
.map { println("processing $it"); it }
.first { it > 3 }
println("Found $result")
}Потоковая обработка поэлементно
В последовательности элемент 1 проходит через отображение, затем через фильтрацию, после чего обрабатывается элемент 2, и так далее. Именно такой порядок обеспечивает досрочное завершение и позволяет не буферизовать все результаты.
fun main() {
listOf(1, 2, 3, 4).asSequence()
.filter { println("filter $it"); it % 2 == 0 }
.map { println("map $it"); it * 10 }
.toList()
}takeWhile и dropWhile
takeWhile выдаёт элементы, пока предикат не станет ложным; dropWhile пропускает начальные элементы, пока предикат остаётся истинным. Обе операции являются ленивыми.
fun main() {
val nums = generateSequence(1) { it + 1 }
println(nums.takeWhile { it < 5 }.toList())
}Избегаем лишних вычислений
Поскольку выполнение начинается только при обработке, а обработка может завершиться досрочно, дорогостоящие преобразования применяются только к элементам, которые действительно доходят до завершающей операции.
fun main() {
val result = (1..100).asSequence()
.map { it * 2 }
.filter { it > 10 }
.take(2)
.toList()
println(result)
}Повторное использование последовательностей
Большинство последовательностей можно перебирать несколько раз, повторно запуская конвейер при каждом переборе. Но последовательности из одноразовых источников, например итератора, можно обработать только один раз. Учитывайте источник.
fun main() {
val seq = sequenceOf(1, 2, 3).map { it * 2 }
println(seq.toList())
println(seq.sum())
}Операции с состоянием и без состояния
Большинство операций не имеют состояния: они обрабатывают по одному элементу за раз. Некоторые операции, например sorted или distinct, хранят состояние и должны буферизовать данные или просмотреть все элементы, что уменьшает преимущество ленивого выполнения.
fun main() {
val result = sequenceOf(3, 1, 2, 1).distinct().sorted().toList()
println(result)
}Преимущества ленивого выполнения
Ленивые операции экономят память, пропуская промежуточные списки, и экономят время благодаря досрочному завершению. Чем больше данных и чем сильнее завершающая операция сокращает обработку, тем заметнее преимущество.
Быстрая проверка
Когда именно выполняются лямбды в цепочке последовательности?
Итоги
Ленивые операции строят конвейер, который запускается только тогда, когда завершающая операция обрабатывает его. Это позволяет избежать промежуточных списков и использовать досрочное завершение с помощью first, take и takeWhile. Помните об операциях с состоянием, таких как sorted. Далее Вы определите, когда последовательности лучше коллекций.
Часто задаваемые вопросы
Урок «Отложенные операции» бесплатный?
Да — полный текст урока «Отложенные операции» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «Отложенные операции»?
Избегайте промежуточных списков Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Отложенные операции»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Зачем нужны последовательности
- Создание последовательностей
- Отложенные операции
- Последовательности и коллекции