Освой Kotlin играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Коллекции в Kotlin используют множество различных интерфейсов: Iterable, Collection, Set, List, MutableIterable, MutableCollection и др.
В Kotlin нет собственных коллекций, только Java-коллекции. Но при этом в Kotlin они обладают более широкими возможностями, используя расширения. Например, вы можете узнать последний элемент списка или найти максимальное значение в коллекции чисел.
Важная особенность - в Kotlin интерфейсы явно разделены на две группы - изменяемые и неизменяемые. Старайтесь всегда использовать неизменяемые коллекции с доступом для чтения. Если вам нужно изменять коллекцию, то тогда выбирайте другой вариант. Здесь не надо путать с var и val. Если вы создадите список для чтения и привяжете его к переменной var, список всё равно нельзя будет изменить после создания.
Для создания различных типов коллекций есть разные функции.
Функция associate() позволяет получить Map, используя поля класса как ключ и значение. Например, в нашем случае имена станут ключами, а возраст - ассоциированными значениями.
val cats = listOf(
Cat("Barsik", 5, 1),
Cat("Murzik", 9, 2)
)
val associatedMap = cats.associate { Pair(it.name, it.age) }
println(associatedMap)
// Результат
{Barsik=5, Murzik=9}
Функция associateTo() - Зададим отображение originalMap, где ключ является строкой, а значением число. Берём список и применяем наше отображение, указывая нужные поля класса (имя и вес).
val originalMap = mutableMapOf<String, Int>()
cats.associateTo(originalMap) { it.name to it.weight }
println(originalMap)
// Результат
{Barsik=1, Murzik=2}
Функция flatMap() работает с коллекцией, содержащей коллекции, и возвращает объединённую «плоскую» коллекцию, содержащую все элементы исходных коллекций.
val result = listOf(listOf(1, 2, 3), listOf(4, 5, 6)).flatMap { it }
println(result) // [1, 2, 3, 4, 5, 6]
Функция map() проходит по каждому элементу и делает операции с элементом в лямбде-выражении, объединяя результаты в новую коллекцию.
Умножаем каждый элемент списка на себя.
var list = listOf(1, 2, 3)
var dest = list.map { it * it }
println(dest.toString()) // выводит [1, 4, 9]
Выводим только имена, игнорируя возраст и другие поля класса.
val cats = listOf(Cat("Barsik", 5, true),
Cat("Murzik", 9, true))
println(cats.map (Cat::name))
// или println(cats.map {it.name})
Можно объединить вызовы функций в цепочки. Выводим имена котов, чей возраст больше 5.
val cats = listOf(Cat("Barsik", 5, true),
Cat("Murzik", 9, true))
println(cats.filter {it.age > 5}.map (Cat::name))
Удаляем префикс у строк списка.
val cats = listOf("!Мурзик", "!Рыжик", "Барсик")
val shortCats = cats.map { name ->
name.removePrefix("!")
}
println(shortCats)
Если при переборе нужна информация об индексах, то используйте mapIndexed().
var list = listOf(8, 4, 2)
var dest = list.mapIndexed { index, i -> "$index: $i"}
println(dest.toString()) // выводит [0: 8, 1: 4, 2: 2]
Если нужно вернуть коллекцию без элементов null, то используйте mapNotNull().
val list = listOf("Барсик", null, "Васька", null, "Мурзик")
val dest = list.mapNotNull { it?.length }
println(dest.toString()) // выводит [6, 6, 6]
val set = setOf("Барсик", null, "Васька", null, "Мурзик")
val newSet = set.mapNotNull { it?.last() }
println(newSet.toString()) // выводит [к, а, к]
Также есть функция mapIndexedNotNull().
Функция-комбинатор zip() принимает разные коллекции и объединяют их в одну новую.
Для примера объединим два списка: с именами котов и их размерами. Функция zip() возвращает новый список (коллекцию пар Pair). Для этой коллекции пар вызовем функцию toMap(), чтобы получить ассоциативный массив, к элементам которого можно обращаться по ключу. В этом случае ключ — имя кота.
val names = listOf("Барсик", "Мурзик", "Рыжик")
val sizes = listOf("большой", "средний", "совсем котёнок")
val catMap = names.zip(sizes).toMap()
// обращаемся к ключу
println(catMap["Рыжик"]) // совсем котёнок
Функция unzip() является полной противоположностью функции zip(). Если у нас есть список пар, то функция разобьёт пары на два списка - в первом будут первые элементы пары, во втором будут вторые элементы каждой пары.
val pairs = listOf(Pair("Россия", "рубль"), Pair("США", "доллар"), Pair("Украина", "гривна"))
println(pairs.unzip().toString()) // выводит ([Россия, США, Украина], [рубль, доллар, гривна])