Урок 8. Коллекции — List, MutableList, Set и Map
Одна переменная хранит одно значение. Когда значений много — оценки класса, список покупок, телефоны друзей, — их кладут в коллекцию. В Kotlin три основные коллекции: список (List), множество (Set) и словарь (Map).
Список: listOf
Список — значения по порядку, у каждого есть номер (индекс). Нумерация начинается с нуля:
fun main() {
val fruits = listOf("apple", "banana", "cherry")
println(fruits)
println(fruits[0])
println(fruits.size)
println(fruits.last())
println("banana" in fruits)
println(fruits.indexOf("cherry"))
}[apple, banana, cherry]
apple
3
cherry
true
2fruits[0]— первый элемент,fruits[2]— третий, последний;size— сколько элементов;first()иlast()— первый и последний;"banana" in fruits— есть ли такой элемент (trueилиfalse);indexOf— номер элемента, или-1, если его нет.
Обращение к несуществующему номеру — fruits[3] в списке из трёх элементов — роняет программу с ошибкой IndexOutOfBoundsException (или похожей ArrayIndexOutOfBoundsException). Безопасно — fruits.getOrNull(3): вернёт null.
Изменяемый список: mutableListOf
Список из listOf изменить нельзя: в нём нет add и remove. Для изменяемого списка есть mutableListOf:
fun main() {
val nums = mutableListOf(5, 3, 8)
nums.add(1)
nums.add(0, 10)
println(nums)
nums.remove(3)
println(nums)
nums.removeAt(0)
nums[0] = 7
println(nums)
println(nums.size)
}[10, 5, 3, 8, 1]
[10, 5, 8, 1]
[7, 8, 1]
3| Действие | Что делает |
|---|---|
add(x) | добавить в конец |
add(i, x) | вставить на место i, остальные сдвигаются |
remove(x) | удалить первый элемент, равный x |
removeAt(i) | удалить элемент с номером i |
list[i] = x | заменить элемент с номером i |
clear() | удалить всё |
Обратите внимание: список объявлен через val, а меняется. val запрещает положить в переменную другой список, но не менять содержимое этого. Пустой список пишут с типом: val names = mutableListOf<String>() — компилятору иначе неоткуда узнать, что в нём будет.
Перебор списка
fun main() {
val prices = listOf(120, 45, 300)
var total = 0
for (p in prices) {
total += p
}
println(total)
for (i in prices.indices) {
println("$i: ${prices[i]}")
}
}465
0: 120
1: 45
2: 300prices.indices — диапазон номеров списка, здесь 0..2. Другие способы перебрать с номером — в справке Как перебрать список с индексом.
Операции над списками
Многие задачи со списками решаются без циклов — готовыми функциями. В фигурных скобках им передают лямбду: что сделать с каждым элементом, а сам элемент внутри неё называется it:
fun main() {
val numbers = listOf(4, 7, 1, 10, 3, 8)
println(numbers.filter { it % 2 == 0 })
println(numbers.map { it * it })
println(numbers.sum())
println(numbers.max())
println(numbers.count { it > 5 })
println(numbers.sorted())
println(numbers.joinToString(" "))
println(numbers.filter { it > 3 }.map { it * 10 })
}[4, 10, 8]
[16, 49, 1, 100, 9, 64]
33
10
3
[1, 3, 4, 7, 8, 10]
4 7 1 10 3 8
[40, 70, 100, 80]| Функция | Что делает |
|---|---|
filter { условие } | новый список из элементов, для которых условие верно |
map { выражение } | новый список: каждый элемент превращён по выражению |
sum(), max(), min() | сумма, наибольший, наименьший |
count { условие } | сколько элементов подходит |
sorted() | новый список по возрастанию |
joinToString(" ") | все элементы в одну строку через разделитель |
any { }, all { } | есть ли хоть один подходящий, подходят ли все |
Эти функции не меняют исходный список, а возвращают новый — numbers после них прежний. Их можно ставить цепочкой: filter { it > 3 }.map { it * 10 } — сначала отобрать, потом превратить. Как сортировать по-разному — в справке Как отсортировать список.
max() и min() у пустого списка бросают ошибку; если список может быть пустым, берите maxOrNull() — вернёт null.
Множество: Set
Множество хранит значения без повторов, номеров у элементов нет:
fun main() {
val visited = mutableSetOf("Minsk", "Brest")
visited.add("Minsk")
visited.add("Grodno")
println(visited)
println(visited.size)
println("Brest" in visited)
val unique = listOf(3, 1, 3, 2, 1).toSet()
println(unique)
}[Minsk, Brest, Grodno]
3
true
[3, 1, 2]Второй "Minsk" не добавился — он уже есть. toSet() — быстрый способ убрать повторы из списка. Множества из setOf и mutableSetOf выводятся в том порядке, в каком элементы добавлялись.
Словарь: Map
Словарь хранит пары «ключ — значение»: по ключу (имени) быстро находится значение (возраст). Пара пишется через to:
fun main() {
val ages = mutableMapOf("Anna" to 20, "Oleg" to 25)
ages["Ivan"] = 18
ages["Anna"] = 21
println(ages)
println(ages["Oleg"])
println(ages["Petr"])
println(ages.getOrDefault("Petr", 0))
for ((name, age) in ages) {
println("$name: $age")
}
}{Anna=21, Oleg=25, Ivan=18}
25
null
0
Anna: 21
Oleg: 25
Ivan: 18ages["Ivan"] = 18— добавить пару, а если ключ уже есть, заменить значение (как с"Anna");ages["Oleg"]— значение по ключу. Ключа может не быть, поэтому тип результата —Int?, и для"Petr"получаетсяnull;getOrDefault(ключ, значение)— значение или запасное, если ключа нет;for ((name, age) in ages)— перебор пар: ключ и значение сразу в две переменные.
Неизменяемый словарь — mapOf(...), изменяемый — mutableMapOf(...), как и у списков.
Массивы
В Kotlin есть и массивы — arrayOf(1, 2, 3), IntArray(5) (пять нулей). Размер массива задаётся при создании и не меняется. Для учебных задач обычно удобнее списки. Если всё же выводите массив, превратите его в текст через contentToString(): println(arr) напечатает не числа, а служебную запись вида [I@1b6d3586.
Коротко
listOf— список только для чтения,mutableListOf— изменяемый:add,remove,removeAt.- Номера элементов — с нуля:
list[0], последний —list[list.size - 1]илиlist.last(). filter,map,sorted,count,sumвозвращают новый результат и не меняют список.Set— без повторов,Map— пары «ключ — значение» черезto;map[key]может датьnull.valу коллекции не мешает менять её содержимое, если онаmutable….