Коллекции. Общий обзор
Стандартная библиотека Kotlin предоставляет большой набор инструментов для работы с коллекциями — группами с переменным количеством элементов (или нулём элементов), которые используются для решения какой-либо задачи.
Коллекции — это общая концепция для большинства языков программирования, поэтому если вы знакомы с коллекциями, например, в Java или Python, то можете пропустить данное введение и перейти к разделам с более подробным описанием.
Обычно в коллекции находится несколько объектов одного типа (но также коллекция может быть пустой). Эти объекты называются элементами или items. Например, все студенты одного факультета образуют коллекцию, которую можно использовать для расчёта их среднего возраста.
- List (список) — упорядоченная коллекция, в которой к элементам можно обращаться по индексам — целым числам, отражающим положение элементов в коллекции. Идентичные элементы (дубликаты) могут встречаться в списке более одного раза. Примером списка является предложение: это группа слов, их порядок важен, и они могут повторяться.
- Set (множество) — коллекция уникальных элементов. Отражает математическую абстракцию множества: группа объектов без повторов. Как правило, порядок расположения элементов здесь не имеет значения. Примером множества является алфавит.
- Map (словарь, ассоциативный список) — набор из пар «ключ-значение». Ключи уникальны и каждый из них соответствует ровно одному значению. Значения могут иметь дубликаты. Ассоциативные списки полезны для хранения логических связей между объектами, например, ID сотрудников и их должностей.
Kotlin позволяет управлять коллекциями независимо от того, какой именно тип объектов в них хранится: будь то String , Int или какой-то собственный класс, общие принципы работы с коллекцией всегда неизменны. Стандартная библиотека Kotlin предоставляет общие интерфейсы, классы и функции для создания, заполнения и управления коллекциями любого типа.
Интерфейсы коллекций и связанные с ними функции находятся в пакете kotlin.collections . Давайте рассмотрим его содержимое.
Типы коллекций
Стандартная библиотека Kotlin предоставляет реализации для основных типов коллекций: Set , List , Map . Есть два вида интерфейсов, предоставляющих каждый из этих типов:
- неизменяемый ( read-only) — предоставляет операции, которые дают доступ к элементам коллекции.
- изменяемый (mutable) — расширяет предыдущий интерфейс и дополнительно даёт доступ к операциям добавления, удаления и обновления элементов коллекции.
Обратите внимание, что изменяемую коллекцию не требуется объявлять с помощью ключевого слова var . Связано это с тем, что изменения вносятся в изначальные объекты коллекции без изменения ссылки на саму коллекцию. Но если вы объявите коллекцию с помощью val и попытаетесь ее перезаписать, то получите ошибку компиляции.
` anywhere the `List ` is required. In other words, the collection types have the same subtyping relationship as the element types. Maps are covariant on the value type, but not on the key type. —>
Неизменяемые типы коллекций ковариантны. Это означает, что если класс Rectangle наследуется от Shape , вы можете использовать List там, где требуется List . Другими словами, типы коллекций имеют такое же отношение подтипов, что и типы элементов. Map -ы ковариантны по типу значения, но не по типу ключа.
` was a subtype of `MutableList `, you could insert other `Shape` inheritors (for example, `Circle`) into it, thus violating its `Rectangle` type argument. —>
В свою очередь, изменяемые коллекции не являются ковариантными; в противном случае это привело бы к сбоям во время выполнения. Если MutableList был подтипом MutableList , вы могли добавить в него других наследников Shape (например, Circle ), таким образом нарушая изначальный тип коллекции — Rectangle .
Ниже представлена схема интерфейсов коллекций Kotlin:
Пройдемся по интерфейсам и их реализациям.
Collection
`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-collection/index.html) is the root of the collection hierarchy. This interface represents the common behavior of a read-only collection: retrieving size, checking item membership, and so on. `Collection` inherits from the `Iterable ` interface that defines the operations for iterating elements. You can use `Collection` as a parameter of a function that applies to different collection types. For more specific cases, use the `Collection`’s inheritors: [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) and [`Set`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/index.html). —>
fun printAll(strings: Collection) < for(s in strings) print("$s ") println() >fun main() < val stringList = listOf("one", "two", "one") printAll(stringList) // one two one val stringSet = setOf("one", "two", "three") printAll(stringSet) // one two three >
`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-collection/index.html) is a `Collection` with write operations, such as `add` and `remove`. —>
fun List.getShortWordsTo(shortWords: MutableList, maxLength: Int) < this.filterTo(shortWords) < it.length // 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() words.getShortWordsTo(shortWords, 3) println(shortWords) // [ago, in, far, far] >
List
`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) stores elements in a specified order and provides indexed access to them. Indices start from zero – the index of the first element – and go to `lastIndex` which is the `(list.size — 1)`. —>
fun main() < val numbers = listOf("one", "two", "three", "four") println("Number of elements: $") // 4 println("Third element: $") // three println("Fourth element: $") // four println("Index of element \"two\" $") // 1 >
Элементы списка (в том числе null ) могут дублироваться: список может содержать любое количество одинаковых объектов. Два списка считаются равными, если они имеют одинаковый размер и их элементы в одних и тех позициях структурно равны.
data class Person(var name: String, var age: Int) fun main() < 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) // true bob.age = 32 println(people == people2) // false >
`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/index.html) is a `List` with list-specific write operations, for example, to add or remove an element at a specific position. —>
Как видите, в некоторых аспектах списки очень похожи на массивы. Однако есть одно важное отличие: размер массива определяется при инициализации и никогда не изменяется; в свою очередь список не имеет предопределённого размера; размер списка может быть изменён в результате операций записи: добавления, обновления или удаления элементов.
По умолчанию в Kotlin реализацией List является ArrayList , который можно рассматривать как массив с изменяемым размером.
Set
`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/index.html) stores unique elements; their order is generally undefined. `null` elements are unique as well: a `Set` can contain only one `null`. Two sets are equal if they have the same size, and for each element of a set there is an equal element in the other set. —>
fun main() < val numbers = setOf(1, 2, 3, 4) println("Number of elements: $") // Number of elements: 4 if (numbers.contains(1)) println("1 is in the set") val numbersBackwards = setOf(4, 3, 2, 1) println("The sets are equal: $") // true >
MutableSet — это Set с операциями записи из MutableCollection .
По умолчанию реализацией Set является LinkedHashSet , который сохраняет порядок вставки элементов. Следовательно, функции, которые зависят от порядка элементов, такие как first() или last() , возвращают предсказуемые результаты для таких множеств.
Альтернативная реализация — HashSet — не сохраняет порядок элементов, поэтому при вызове функций first() или last() вернётся непредсказуемый результат. Однако HashSet требует меньше памяти для хранения того же количества элементов.
Map
`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/index.html) is not an inheritor of the `Collection` interface; however, it’s a Kotlin collection type as well. A `Map` stores _key-value_ pairs (or _entries_); keys are unique, but different keys can be paired with equal values. The `Map` interface provides specific functions, such as access to value by key, searching keys and values, and so on. —>
fun main() < val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1) println("All keys: $") // Kotlin коллекция с уникальными значениями println("All values: $") // [1, 2, 3, 1] if ("key2" in numbersMap) println("Value by key \"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") // аналогичен предыдущему условию >
Две Map -ы, содержащие равные пары, равны независимо от порядка пар.
fun main() < 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: $") // The maps are equal: true >
MutableMap — это Map с операциями записи, например, можно добавить новую пару «ключ-значение» или обновить значение, связанное с указанным ключом.
По умолчанию реализацией Map является LinkedHashMap — сохраняет порядок элементов. Альтернативная реализация — HashMap — не сохраняет порядок элементов.
© 2015—2023 Open Source Community
Коллекции в Kotlin — списки, словари и множества
В Kotlin есть три основных разновидности коллекций – список (List), словарь (Map) и множество (Set). Множества содержат только уникальные значения, в них не может быть двух элементов с одинаковыми значениями, а порядок элементов не важен.
Коллекции – это классы-дженерики, поэтому при создании объектов явно указывается или компилятор сам выводит тип составляющих коллекцию объектов. Примеры объявлений переменных типов коллекций без связывания их с объектами:
fun main() val c: ListInt> val d: MapString, Int> val e: SetByte> >
Здесь список и множество состоят из целых чисел, в словаре ключами являются строки, а значениями числа. Тип может быть родительским. Тогда коллекция может содержать любой дочерний тип данных, однако необходимость в подобном возникает редко.
Обычно коллекции создаются не с помощью конструкторов, а вызовом специальных функций языка Kotlin – listOf() , mapOf() , setOf() .
fun main() val c = listOf(1, 4, 6, 1) val d = mapOf("apple" to 10, "orange" to 5) val e = setOf(3, 4, 5, 0) >
Типы переменных можно опустить, так как компилятор способен их вывести сам из объектов.
Список можно создать с помощью конструктора. Это удобно, когда список длинный или заполняется одинаковыми значениями.
В примере будет создан список из десяти элементов, каждый элемент будет равен нулю. В фигурных скобках находится лямбда-выражение, которое может быть сложнее.
В Kotlin интерфейсы List , Map и Set относятся к неизменяемым коллекциям. Другими словами, все, что приведено выше, не допускает изменения своих элементов.
Если нужны изменяемые коллекции, создавать их следует от MutableList , MutableMap и MutableSet . Соответственно используются функции mutableListOf() , mutableMapOf() , mutableSetOf() . Изменяемые интерфейсы наследуют от своих неизменяемых интерфейсов + интерфейс MutableCollection (только для MutableList и MutableSet ).
fun main() val c = MutableList(10)0> c[0] = 109 val a = mutableListOfInt>() a.add(40) val b = mutableMapOf('a' to 10, 'b' to 5) b['c'] = 12 for ((key, value) in b) println("$key = $value") >
Коллекции относятся к итерируемым объектам, то есть у них есть метод iterator() , вызов которого создает объект-итератор. Этот метод вызывается неявно в заголовке цикла for .
Класс ArrayList – конкретная реализация изменяемого списка. Используется по умолчанию. Его конструктор несколько иной, лямбда ему не передается.
fun main() val a = ArrayListString>() a.add("Hello") a.add("World") val b = setOf("hi", "oh") val c = ArrayList(b) >