Обзор коллекций
Стандартная библиотека Kotlin предоставляет полный набор инструментов для работы с коллекциями — группами переменного числа элементов (возможно, нулевого), важных для решаемой задачи и часто используемых в работе.
Коллекции — распространённое понятие в большинстве языков программирования, поэтому, если вы знакомы, например, с коллекциями Java или Python, можете пропустить это введение и перейти к подробным разделам.
Коллекция обычно содержит несколько объектов одного типа (и его подтипов). Объекты в коллекции называются элементами. Например, все студенты кафедры образуют коллекцию, которую можно использовать для вычисления их среднего возраста.
Для Kotlin важны следующие типы коллекций:
Список (List) — упорядоченная коллекция с доступом к элементам по индексам — целым числам, отражающим их положение. Элементы могут встречаться в списке несколько раз. Пример списка — номер телефона: это группа цифр, порядок которых важен, и они могут повторяться.
Множество (Set) — коллекция уникальных элементов. Оно отражает математическое понятие множества: группы объектов без повторений. Как правило, порядок элементов множества не имеет значения. Например, номера на лотерейных билетах образуют множество: они уникальны, и их порядок не важен.
Отображение (Map) (или словарь) — набор пар «ключ-значение». Ключи уникальны, и каждому из них соответствует ровно одно значение. Значения могут повторяться. Отображения полезны для хранения логических связей между объектами, например идентификатора сотрудника и его должности.
Kotlin позволяет работать с коллекциями независимо от конкретного типа хранящихся в них объектов. Другими словами, добавить String в список String можно так же, как Int или экземпляр класса, определённого пользователем. Поэтому стандартная библиотека Kotlin предлагает обобщённые интерфейсы, классы и функции для создания, заполнения и управления коллекциями любого типа.
Интерфейсы коллекций и связанные с ними функции находятся в пакете kotlin.collections. Давайте рассмотрим его содержимое.
Типы коллекций
Стандартная библиотека Kotlin предоставляет реализации основных типов коллекций: множеств, списков и отображений. Каждый тип коллекции представлен парой интерфейсов:
Интерфейс только для чтения, предоставляющий операции для доступа к элементам коллекции.
Изменяемый интерфейс, который расширяет соответствующий интерфейс только для чтения операциями записи: добавлением, удалением и обновлением элементов.
Обратите внимание: изменяемую коллекцию не обязательно присваивать var. Операции записи с изменяемой коллекцией по-прежнему возможны, даже если она присвоена val. Преимущество присваивания изменяемых коллекций val состоит в том, что это защищает ссылку на изменяемую коллекцию от изменений. По мере роста и усложнения кода предотвращать непреднамеренное изменение ссылок становится ещё важнее. Используйте val как можно чаще, чтобы писать более безопасный и надёжный код. Если попытаться повторно присвоить коллекцию val, возникнет ошибка компиляции:
fun main() {
//sampleStart
val numbers = mutableListOf("one", "two", "three", "four")
numbers.add("five") // this is OK
println(numbers)
//numbers = mutableListOf("six", "seven") // compilation error
//sampleEnd
}
Типы коллекций только для чтения являются ковариантными. Это означает, что если класс Rectangle наследуется от Shape, то List<Rectangle> можно использовать везде, где требуется List<Shape>. Другими словами, типы коллекций имеют такое же отношение подтипов, как и типы элементов. Отображения ковариантны по типу значения, но не по типу ключа.
Изменяемые коллекции, в свою очередь, не являются ковариантными, иначе это привело бы к ошибкам во время выполнения. Если бы MutableList<Rectangle> был подтипом MutableList<Shape>, в него можно было бы добавлять другие классы, наследующие Shape (например, Circle), нарушая тем самым его аргумент типа Rectangle.
Ниже приведена схема интерфейсов коллекций Kotlin:
Рассмотрим интерфейсы и их реализации. Чтобы узнать о Collection, прочитайте следующий раздел. О List, Set и Map можно прочитать в соответствующих разделах или посмотреть видео Себастьяна Айгнера, защитника Kotlin:
Collection
Collection<T> — корневой интерфейс иерархии коллекций. Этот интерфейс описывает общее поведение коллекции только для чтения: получение размера, проверку наличия элемента и т. д. Collection наследуется от интерфейса Iterable<T>, определяющего операции перебора элементов. Collection можно использовать в качестве параметра функции, работающей с разными типами коллекций. Для более конкретных случаев используйте наследников Collection: List и Set.
fun printAll(strings: Collection<String>) {
for(s in strings) print("$s ")
println()
}
fun main() {
val stringList = listOf("one", "two", "one")
printAll(stringList)
val stringSet = setOf("one", "two", "three")
printAll(stringSet)
}
MutableCollection<T> — это Collection с операциями записи, такими как add и remove.
fun List<String>.getShortWordsTo(shortWords: MutableList<String>, maxLength: Int) {
this.filterTo(shortWords) { it.length <= maxLength }
// throwing away the articles
val articles = setOf("a", "A", "an", "An", "the", "The")
shortWords -= articles
}
fun main() {
val words = "A long time ago in a galaxy far far away".split(" ")
val shortWords = mutableListOf<String>()
words.getShortWordsTo(shortWords, 3)
println(shortWords)
}
List
List<T> хранит элементы в заданном порядке и обеспечивает доступ к ним по индексам. Индексы начинаются с нуля — индекса первого элемента — и доходят до lastIndex, который является (list.size - 1).
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println("Number of elements: ${numbers.size}")
println("Third element: ${numbers.get(2)}")
println("Fourth element: ${numbers[3]}")
println("Index of element \"two\" ${numbers.indexOf("two")}")
//sampleEnd
}
Элементы списка (включая null) могут повторяться: список может содержать любое количество одинаковых объектов или экземпляров одного объекта. Два списка считаются равными, если у них одинаковый размер и структурно равные элементы стоят на одинаковых позициях.
data class Person(var name: String, var age: Int)
fun main() {
//sampleStart
val bob = Person("Bob", 31)
val people = listOf(Person("Adam", 20), bob, bob)
val people2 = listOf(Person("Adam", 20), Person("Bob", 31), bob)
println(people == people2)
bob.age = 32
println(people == people2)
//sampleEnd
}
MutableList<T> — это List со специфичными для списков операциями записи, например добавлением или удалением элемента в определённой позиции.
fun main() {
//sampleStart
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.removeAt(1)
numbers[0] = 0
numbers.shuffle()
println(numbers)
//sampleEnd
}
Как видите, в некоторых аспектах списки очень похожи на массивы. Однако есть одно важное отличие: размер массива задаётся при инициализации и никогда не меняется; список же не имеет предопределённого размера — его размер может меняться в результате операций записи: добавления, обновления или удаления элементов.
В Kotlin стандартная реализация MutableList — ArrayList, которую можно представить как массив с изменяемым размером.
Set
Set<T> хранит уникальные элементы; их порядок, как правило, не определён. Элементы null также уникальны: Set может содержать только один null. Два множества равны, если у них одинаковый размер и для каждого элемента одного множества найдётся равный ему элемент в другом.
fun main() {
//sampleStart
val numbers = setOf(1, 2, 3, 4)
println("Number of elements: ${numbers.size}")
if (numbers.contains(1)) println("1 is in the set")
val numbersBackwards = setOf(4, 3, 2, 1)
println("The sets are equal: ${numbers == numbersBackwards}")
//sampleEnd
}
MutableSet — это Set с операциями записи из MutableCollection.
Стандартная реализация MutableSet — LinkedHashSet — сохраняет порядок добавления элементов. Поэтому функции, зависящие от порядка, такие как first() или last(), возвращают для таких множеств предсказуемые результаты.
fun main() {
//sampleStart
val numbers = setOf(1, 2, 3, 4) // LinkedHashSet is the default implementation
val numbersBackwards = setOf(4, 3, 2, 1)
println(numbers.first() == numbersBackwards.first())
println(numbers.first() == numbersBackwards.last())
//sampleEnd
}
Альтернативная реализация — HashSet — не гарантирует порядок элементов, поэтому вызов таких функций для неё даёт непредсказуемые результаты. Однако HashSet требует меньше памяти для хранения того же числа элементов.
Map
Map<K, V> не является наследником интерфейса Collection, но также относится к типам коллекций Kotlin. Map хранит пары «ключ-значение» (или записи); ключи уникальны, но с разными ключами могут быть связаны одинаковые значения. Интерфейс Map предоставляет специальные функции, например доступ к значению по ключу, поиск ключей и значений и т. д.
fun main() {
//sampleStart
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
println("All keys: ${numbersMap.keys}")
println("All values: ${numbersMap.values}")
if ("key2" in numbersMap) println("Value by key \"key2\": ${numbersMap["key2"]}")
if (1 in numbersMap.values) println("The value 1 is in the map")
if (numbersMap.containsValue(1)) println("The value 1 is in the map") // same as previous
//sampleEnd
}
Два отображения с одинаковыми парами равны независимо от порядка пар.
fun main() {
//sampleStart
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
val anotherMap = mapOf("key2" to 2, "key1" to 1, "key4" to 1, "key3" to 3)
println("The maps are equal: ${numbersMap == anotherMap}")
//sampleEnd
}
MutableMap — это Map с операциями записи для отображений; например, можно добавить новую пару «ключ-значение» или обновить значение, связанное с заданным ключом.
fun main() {
//sampleStart
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
numbersMap["one"] = 11
println(numbersMap)
//sampleEnd
}
Стандартная реализация MutableMap — LinkedHashMap — сохраняет порядок добавления элементов при переборе отображения. Альтернативная реализация — HashMap — не гарантирует порядок элементов.
ArrayDeque
ArrayDeque<T> — реализация двусторонней очереди, которая позволяет добавлять и удалять элементы как в начале, так и в конце очереди. Таким образом, ArrayDeque в Kotlin также выполняет роль структур данных «стек» и «очередь». Внутри ArrayDeque реализована на основе массива с изменяемым размером, который при необходимости автоматически подстраивается:
fun main() {
val deque = ArrayDeque(listOf(1, 2, 3))
deque.addFirst(0)
deque.addLast(4)
println(deque) // [0, 1, 2, 3, 4]
println(deque.first()) // 0
println(deque.last()) // 4
deque.removeFirst()
deque.removeLast()
println(deque) // [1, 2, 3]
}
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/collections-overview.html