Spec-Zone.ru › Kotlin 1.6

Обзор коллекций

Стандартная библиотека Kotlin предоставляет полный набор инструментов для управления коллекциями — группами переменного числа элементов (возможно, нулевого), которые важны для решаемой задачи и над которыми обычно производятся операции.

Коллекции — это общее понятие для большинства языков программирования, поэтому, если вы знакомы, например, с коллекциями Java или Python, вы можете пропустить это введение и перейти к подробным разделам.

Коллекция обычно содержит некоторое количество объектов (это число также может быть нулевым) одного типа. Объекты в коллекции называются элементами или объектами. Например, все студенты одного факультета образуют коллекцию, которую можно использовать для расчета их среднего возраста.

Следующие типы коллекций актуальны для Kotlin:

  • Список — упорядоченная коллекция с доступом к элементам по индексам — целым числам, отражающим их позицию. Элементы могут встречаться более одного раза в списке. Пример списка — телефонный номер: это группа цифр, их порядок важен, и они могут повторяться.

  • Множество — коллекция уникальных элементов. Она отражает математическую абстракцию множества: группа объектов без повторений. Как правило, порядок элементов множества не имеет значения. Например, номера на лотерейных билетах образуют множество: они уникальны, и их порядок не важен.

  • Карта (или словарь) — набор пар ключ-значение. Ключи уникальны, и каждый из них сопоставляется ровно с одним значением. Значения могут дублироваться. Карты полезны для хранения логических связей между объектами, например, идентификатора сотрудника и его должности.

Kotlin позволяет манипулировать коллекциями независимо от точного типа объектов, хранящихся в них. Другими словами, вы добавляете String в список String так же, как и с Int или с классом пользовательского определения. Таким образом, стандартная библиотека Kotlin предлагает обобщенные интерфейсы, классы и функции для создания, заполнения и управления коллекциями любого типа.

Интерфейсы коллекций и связанные функции находятся в пакете kotlin.collections. Давайте рассмотрим его содержимое.

Типы коллекций

Стандартная библиотека Kotlin предоставляет реализации основных типов коллекций: множеств, списков и карт. Каждый тип коллекции представлен парой интерфейсов:

  • Интерфейс только для чтения, предоставляющий операции для доступа к элементам коллекции.

  • Интерфейс изменяемый, который расширяет соответствующий интерфейс только для чтения, добавляя операции записи: добавление, удаление и обновление элементов.

Обратите внимание, что изменение изменяемой коллекции не требует, чтобы она была var: операции записи изменяют тот же объект изменяемой коллекции, поэтому ссылка не меняется. Хотя, если вы попытаетесь переназначить 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 interfaces hierarchy

Давайте пройдемся по интерфейсам и их реализациям. Чтобы узнать о Collection, прочитайте раздел ниже. Чтобы узнать о List, Set, и Map, вы можете либо прочитать соответствующие разделы, либо посмотреть видео Себастьяна Айгнера, разработчика Kotlin:

Коллекция

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<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 по умолчанию реализацией List является ArrayList, которую можно рассматривать как изменяемый массив.

Множество

Set<T> хранит уникальные элементы; их порядок обычно не определен. Элементы null также уникальны: множество может содержать только один 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.

По умолчанию реализация Set — 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<K, V> не является наследником интерфейса Collection; однако это тоже тип Kotlin коллекции. Карта хранит пары ключ-значение (или записи); ключи уникальны, но разные ключи могут быть сопоставлены с одинаковыми значениями. Интерфейс 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
}

По умолчанию реализация Map — LinkedHashMap — сохраняет порядок вставки элементов при итерировании по карте. В свою очередь, альтернативная реализация — HashMap — ничего не говорит о порядке элементов.

Последнее изменение: 07 апреля 2022
Дорожная карта Kotlin Создание коллекций

© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/collections-overview.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API