Коллекции и Sequence в Kotlin
Дальше: Типы данных и переменные · Справочник Kotlin
Коллекции и Sequence
В программе редко хватает одной переменной — нужны списки заказов, словари «id → пользователь», множества уникальных тегов. В Kotlin для этого есть готовые типы в стандартной библиотеке.
Эта статья объясняет:
- чем отличаются
List,Set,Map; - зачем неизменяемые и изменяемые версии;
- когда подключать ленивый Sequence;
- как не словить типичные ошибки при итерации.
Справочник функций: встроенные функции. Асинхронные потоки во времени — отдельно Flow.
Словарь терминов
| Термин | Простыми словами |
|---|---|
| Коллекция | Структура из многих элементов (список, множество, карта). |
| List | Упорядоченный список; допускает повторы; доступ по индексу 0, 1, 2… |
| Set | Множество уникальных элементов без порядка (или с порядком в LinkedHashSet). |
| Map | Пары «ключ → значение»: как телефонная книга по имени. |
Иммутабельный (listOf) | После создания «как будто нельзя менять» — изменения дают новую коллекцию. |
Мутабельный (mutableListOf) | Можно add, remove на месте. |
| Iterable | То, по чему можно пройти for (x in coll). |
| Sequence | Ленивая цепочка: элементы обрабатываются по одному, без лишних промежуточных списков. |
| Терминальный оператор | Завершает цепочку: toList(), count(), first() — после него Sequence «отработал». |
Сводка операций по типам
MutableList:
| Действие | Метод |
|---|---|
| Добавить в конец | add(value) |
| Вставить по индексу | add(index, value) |
| Прочитать | get(index), [index] |
| Заменить | set(index, value), [index] = value |
| Удалить по индексу | removeAt(index) |
MutableMap:
| Действие | Метод |
|---|---|
| Добавить или заменить | put(key, value), [key] = value |
| Прочитать | get(key), [key] |
| Удалить | remove(key) |
MutableSet:
| Действие | Метод |
|---|---|
| Добавить | add(value) |
| Удалить | remove(value) |
| Проверить наличие | contains(value) |
Неизменяемые listOf / mapOf / setOf поддерживают чтение; «изменение» — новая коллекция (+, buildList, копия в mutable*).
Когда какой тип выбрать
| Задача | Тип |
|---|---|
| Список задач по порядку | List |
| Уникальные теги, посещённые id | Set |
| Поиск по ключу (id пользователя) | Map |
| Большой файл, обработка по строкам | Sequence |
| Обновления из сети/БД во времени | Flow |
Создание коллекций
val readOnly = listOf("a", "b", "c")
val mutable = mutableListOf(1, 2, 3)
val scores = mapOf("Ann" to 90, "Bob" to 85)
val unique = setOf(1, 2, 2, 3) // фактически {1, 2, 3}
| Функция | Можно менять на месте? |
|---|---|
listOf, mapOf, setOf | Нет (интерфейс read-only) |
mutableListOf, mutableMapOf | Да |
Неизменяемость и копии
val base = listOf(1, 2, 3)
val extended = base + 4 // новый List, base не меняется
val mutable = base.toMutableList()
mutable.add(4) // меняется только mutable
Оператор + для коллекций создаёт новый список. На тысячах элементов в цикле это дорого — тогда один раз buildList { }:
val ids = buildList {
addAll(source)
add(newId)
}
Цепочки filter и map на List
data class User(val name: String, val isActive: Boolean)
val users = listOf(
User("Ann", true),
User("Bob", false),
User("Cal", true)
)
val activeNames = users
.filter { it.isActive }
.map { it.name }
Разбор выражения filter { it.isActive }:
filter— оставить элементы, для которых лямбда вернулаtrue.it— текущий элемент (User); имя можно заменить:filter { user -> user.isActive }.- Результат — новый список; исходный
usersне меняется.
У List каждый шаг (filter, map, sorted) строит новую коллекцию — это eager (жадное) выполнение.
Sequence — ленивая цепочка
val result = (1..1_000_000)
.asSequence()
.filter { it % 2 == 0 }
.map { it * it }
.take(10)
.toList()
Пока не вызван toList(), count(), first() и т.п., элементы проходят цепочку по одному: для миллиона чисел не создаётся миллион промежуточных списков — берутся первые 10 подходящих чётных квадратов.
Чтение большого лога:
import java.io.File
fun topErrors(path: String, limit: Int): List<String> =
File(path).useLines { lines ->
lines.asSequence()
.filter { "ERROR" in it }
.take(limit)
.toList()
}
useLines закрывает файл после блока — sequence не должен «жить» дольше открытого потока.
Eager и Lazy — наглядно
// Eager: три полных прохода, три промежуточных списка в памяти
val names = users
.filter { it.isActive }
.map { it.name }
.sorted()
// Lazy: один проход при toList()
val namesLazy = users.asSequence()
.filter { it.isActive }
.map { it.name }
.sorted()
.toList()
List + filter/map | Sequence | |
|---|---|---|
| Когда считается | На каждом шаге сразу | При терминальном операторе |
| Память | Промежуточные списки | Один элемент за раз |
| Когда выбирать | До ~сотен–тысяч элементов, короткая цепочка | Длинная цепочка, большой файл, генератор |
Группировка и Map из списка
data class Order(val region: String, val amount: Int)
val orders = listOf(
Order("EU", 100),
Order("EU", 50),
Order("US", 200)
)
val byRegion = orders.groupBy { it.region }
// Map: "EU" -> [Order(...), Order(...)], "US" -> [...]
val totalByRegion = orders.groupingBy { it.region }
.fold(0) { acc, order -> acc + order.amount }
// Map: "EU" -> 150, "US" -> 200
val userById = users.associateBy { it.name } // ключ — name
groupingBy + fold удобен, когда нужна одна агрегированная цифра на ключ без списка промежуточных элементов.
Типичные ошибки
- Дважды пройти один и тот же
Sequence— послеtoList()исходная последовательность исчерпана; создайте новуюasSequence()или сохраните результат. - Итерировать
mutableListи одновременноremove—ConcurrentModificationException; копируйте список или удаляйте с конца по индексу. - Путать
ListиIntArray— у примитивного массива нетfilterиз stdlib без преобразования. - Держать sequence открытого файла вне
useLines— файл закроется, итерация сломается.
Связанные материалы
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). История Kotlin - создание языка JetBrains для плавной совместимости с Java и современной разработки. Экосистема Kotlin-приложений - инструменты, фреймворки и мультиплатформенные сценарии разработки. Kotlin — это современный язык программирования общего назначения, который работает на платформе Java Virtual Machine (JVM), а также компилируется в JavaScript или нативный код. Набор советов, правил, принципов и обычаев в разработке на этом языке. Основы Kotlin - архитектура проекта, платформенные модули и базовые принципы разработки на языке. Типизация, набор правил определения типа данных значений языка. Операторы и выражения в Kotlin - логика вычислений, приоритеты операций и идиоматичный стиль записи условий. Циклы и управляющие конструкции в Kotlin - идиоматичные способы итерации, условия и управление выполнением. ООП в Kotlin - классы, модификаторы доступа, наследование и идиоматичное проектирование объектных структур. Кавычки, точки, запятые, скобки и прочие знаки препинания. Kotlin использует набор зарезервированных слов для построения синтаксических конструкций языка. Все ключевые слова разделены на категории по назначению. Набор функций, которые включены в стандартную библиотеку языка.История языка Kotlin
Экосистема Kotlin-приложений
Что требуется знать перед началом изучения языка программирования Kotlin
Рекомендации по разработке на Kotlin
Основы языка Kotlin
Типы данных и объявление переменных
Операторы и выражения в Kotlin
Циклы и управляющие конструкции
Объектно-ориентированное программирование в Kotlin
Синтаксис и пунктуация в Kotlin
Ключевые слова языка Kotlin
Встроенные функции и расширения Kotlin