type | layout | category | title | url |
---|---|---|---|---|
doc |
reference |
Other |
Коллекции |
В отличие от многих языков, Kotlin различает изменяемые и неизменяемые коллекции (списки, множества, ассоциативные списки и т.д.). Точный контроль над тем, когда именно коллекции могут быть изменены, полезен для устранения багов и разработки хорошего API.
Важно понимать различие между read-only представлением изменяемой коллекции, и фактически неизменяемой коллекцией. Их легко создать, но вот система типов не выражает различие между ними, поэтому следить за этим должны вы (если это необходимо).
Тип List<out T>
в Kotlin — интерфейс, который предоставляет read-only операции, такие как size
, get
, и другие. Так же, как и в Java,
он наследуется от Collection<T>
, а значит и от Iterable<T>
. Методы, которые изменяют список, добавлены в интерфейс MutableList<T>
.
То же самое относится и к Set<out T>/MutableSet<T>
, Map<K, out V>/MutableMap<K, V>
.
Пример базового использования списка (list) и множества (set):
val numbers: MutableList<Int> = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = numbers
println(numbers) // выведет "[1, 2, 3]"
numbers.add(4)
println(readOnlyView) // выведет "[1, 2, 3, 4]"
readOnlyView.clear() // -> не скомпилируется
val strings = hashSetOf("a", "b", "c", "c")
assert(strings.size == 3)
Kotlin не имеет специальных синтаксических конструкций для создания списков или множеств. Используйте методы из стандартной библиотеки,
такие как listOf()
, mutableListOf()
, setOf()
, mutableSetOf()
.
Создание ассоциативного списка некритичным для производительности способом может быть осуществленно с помощью простой идиомы:
mapOf(a to b, c to d)
Заметьте, что переменная readOnlyView
изменяется вместе со списком, на который она указывает. Если единственная ссылка, указывающая
на список является read-only, то мы можем с уверенностью сказать, что коллекция полностью неизменяемая.
Простой способ создания такой коллекции выглядит так:
val items = listOf(1, 2, 3)
В данный момент, метод listOf
реализован с помощью ArrayList, но не исключено, что в будущем могут быть использованы другие типы коллекций, более эффективные по памяти за счёт своей неизменности.
Заметьте, что read-only типы ковариантны. Это значит, что вы можете взять List<Rectangle>
(список прямоугольников) и присвоить его List<Shape>
(списку фигур) предполагая, что Rectangle наследуется от Shape. Такое присвоение было бы запрещено с изменяемыми коллекциями, потому что в таком случае появляется риск возникновения ошибок времени исполнения.
Иногда вам необходимо вернуть состояние коллекции в определённый момент времени:
class Controller {
private val _items = mutableListOf<String>()
val items: List<String> get() = _items.toList()
}
Расширение toList
просто копирует элементы списка. Таким образом, возвращаемый список гарантированно не изменится.
Существует несколько полезных расширений для списков и множеств, с которыми стоит познакомиться:
val items = listOf(1, 2, 3, 4)
items.first() == 1
items.last() == 4
items.filter { it % 2 == 0 } // возвратит [2, 4]
val rwList = mutableListOf(1, 2, 3)
rwList.requireNoNulls() // возвратит [1, 2, 3]
if (rwList.none { it > 6 }) println("Нет элементов больше 6") // выведет "Нет элементов больше 6"
val item = rwList.firstOrNull()
Также, обратите внимание на такие утилиты как sort
, zip
, fold
, reduce
.
То же самое происходит и с ассоциативными списками. Они могут быть с лёгкостью инициализированы и использованы следующим образом:
val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
println(readWriteMap["foo"]) // выведет "1"
val snapshot: Map<String, Int> = HashMap(readWriteMap)