← Назад к вопросам
Что такое asSequence?
1.7 Middle🔥 151 комментариев
#Kotlin основы#Производительность и оптимизация
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
asSequence() — ленивые вычисления в Kotlin
asSequence() преобразует коллекцию (List, Set, Map и т.д.) в последовательность (Sequence) с ленивой (lazy) обработкой элементов. Это мощный инструмент для оптимизации производительности при работе с большими коллекциями.
List vs Sequence: режимы обработки
Список (List) — eager (спешная) обработка:
val numbers = listOf(1, 2, 3, 4, 5)
// Каждая операция проходит по ВСЕм элементам
val result = numbers
.map { it * 2 } // 5 элементов → [2, 4, 6, 8, 10]
.filter { it > 5 } // Проходит по 5 элементам → [6, 8, 10]
.take(2) // Берет 2 элемента → [6, 8]
// Всего операций: 5 + 5 + 2 = 12 операций
Последовательность (Sequence) — lazy (ленивая) обработка:
val numbers = listOf(1, 2, 3, 4, 5)
// Обработка происходит только для нужных элементов
val result = numbers.asSequence()
.map { it * 2 } // Не вычисляется! (lazy)
.filter { it > 5 } // Не вычисляется! (lazy)
.take(2) // Вычисляются только 2 нужных элемента
.toList() // Только здесь срабатывают все операции
// Фактический порядок вычисления:
// 1: map(1*2)=2 → filter(2>5)=false → skip
// 2: map(2*2)=4 → filter(4>5)=false → skip
// 3: map(3*2)=6 → filter(6>5)=true → take(1)
// 4: map(4*2)=8 → filter(8>5)=true → take(2) → STOP!
// Всего операций: 2 + 2 + 2 = 6 операций (экономия 50%!)
Синтаксис asSequence()
// Способ 1: asSequence() из коллекции
val sequence = listOf(1, 2, 3).asSequence()
// Способ 2: generateSequence() для бесконечной последовательности
val infiniteSequence = generateSequence(0) { it + 1 }
// Способ 3: sequenceOf() для отдельных элементов
val sequence = sequenceOf(1, 2, 3, 4, 5)
// Способ 4: sequence() lambda
val sequence = sequence {
yield(1)
yield(2)
yield(3)
}
Когда asSequence() дает выигрыш
Используй asSequence() когда:
// 1. Много промежуточных операций + take/first
val result = bigList.asSequence()
.map { complexCalculation(it) }
.filter { it.isValid() }
.map { it.transform() }
.filter { it.passes() }
.take(1) // Нужен только один элемент!
.toList()
// 2. Большая коллекция с операциями фильтрации
val largeData = generateSequence(1) { it + 1 }.take(1_000_000)
val filtered = largeData.asSequence()
.filter { it % 2 == 0 }
.map { it / 2 }
.take(100)
.toList()
// 3. Цепочка фильтраций
val result = data.asSequence()
.filter { it.age > 18 }
.filter { it.status == "active" }
.filter { it.balance > 0 }
.take(10)
.toList()
Не используй asSequence() когда:
// 1. Нужны ВСЕ результаты (нет early termination)
val result = list.asSequence() // Неэффективно
.map { it * 2 }
.toList() // Создаст всю коллекцию anyway
// 2. Несколько независимых итераций
val seq = list.asSequence()
val sum = seq.sum() // Первая итерация
val count = seq.count() // Вторая итерация (неэффективно!)
// Лучше:
val list = list
val sum = list.sum()
val count = list.count()
Практические примеры
Пример 1: Поиск первого валидного пользователя
data class User(val id: Int, val name: String, val email: String)
// Без asSequence: проверяет ВСЕх пользователей
val user = users
.map { enrichUserData(it) } // Вызывается для всех
.filter { it.isValid() } // Проверяется для всех
.first() // Берет первого
// С asSequence: проверяет до первого валидного
val user = users.asSequence()
.map { enrichUserData(it) } // Вызывается только до первого
.filter { it.isValid() } // Проверяется только до первого
.first() // Берет первого и СТОПИТ
Пример 2: Обработка файла по строкам
// Ленивое чтение файла
val lines = File("huge_file.txt").readLines().asSequence()
val result = lines
.map { it.trim() }
.filter { it.isNotEmpty() }
.map { parseData(it) }
.filter { it.isValid() }
.take(100) // Читает только нужное
.toList()
Пример 3: Бесконечная последовательность
// Генерируем числа Фибоначчи
val fibonacci = generateSequence(1 to 1) { (a, b) -> b to (a + b) }
.map { it.first }
// Берем первые 10 чисел Фибоначчи
val first10 = fibonacci.take(10).toList()
// [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// Без asSequence пришлось бы создавать конечный список
Производительность: измеренные результаты
import kotlin.system.measureTimeMillis
val hugeList = (1..10_000_000).toList()
// Список (eager)
var time = measureTimeMillis {
val result = hugeList
.map { it * 2 }
.filter { it > 100_000 }
.take(10)
.toList()
}
println("List: ${time}ms") // ~500ms
// Последовательность (lazy)
time = measureTimeMillis {
val result = hugeList.asSequence()
.map { it * 2 }
.filter { it > 100_000 }
.take(10)
.toList()
}
println("Sequence: ${time}ms") // ~10ms (50x быстрее!)
Важные моменты
- Sequence — одноразовая — после toList() нельзя повторно использовать
- Ленивость — операции не выполняются до терминального оператора (toList, first, forEach и т.д.)
- Порядок вычисления — по элементам, не по операциям (как в Stream Java)
- Для Android — особенно полезна при работе с Room, Retrofit, больших обработках
Практическое правило
Если в цепочке есть take(), first(), any(), find() или другие ранние остановки — используй asSequence(). Это один из лучших способов оптимизировать Kotlin код без потери читаемости.