Освой Kotlin играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Коллекции. List (Списки)


Коллекции в Kotlin используют множество различных интерфейсов: Iterable, Collection, Set, List, MutableIterable, MutableCollection и др.

В Kotlin нет собственных коллекций, только Java-коллекции. Но при этом в Kotlin они обладают более широкими возможностями, используя расширения. Например, вы можете узнать последний элемент списка или найти максимальное значение в коллекции чисел.

Важная особенность - в Kotlin интерфейсы явно разделены на две группы - изменяемые и неизменяемые. Старайтесь всегда использовать неизменяемые коллекции с доступом для чтения. Если вам нужно изменять коллекцию, то тогда выбирайте другой вариант. Здесь не надо путать с var и val. Если вы создадите список для чтения и привяжете его к переменной var, список всё равно нельзя будет изменить после создания.

Для создания различных типов коллекций есть разные функции. После создания узнаем, какой класс соответствует коллекции. Основные приёмы работы с коллекциями показаны у listOf().

В некоторых примерах используются объекты класса Cat.


data class Cat(
    val name: String,
    val age: Int,
    val weight: Int
)

List

Функции для создания списков. В списках допустимы дубликаты.

listOf()

Неизменяемые списки List создаются через функцию listOf():


val list = listOf(1, 3, 9)
println(list.javaClass)
// class java.util.Arrays$ArrayList

Компилятор определяет тип объекта, который должен содержаться в списке, по типам всех значений, переданных при создании. Например, список в нашем примере инициализируется тремя числами, поэтому компилятор создаёт List с типом List<Int>. Тип List также можно задать явно:


val cats: List<String>
cats = listOf("Барсик", "Мурзик", "Васька")

Если у вас есть изменяемый список, то его можно сконвертировать в список для чтения через toList():


val cats = mutableListOf("Мурзик", "Барсик", "Рыжик")
val readOnlyCats = cats.toList()
println("${readOnlyCats.first()}")

Доступ к отдельному элементу списка можно получить по номеру индекса через квадратные скобки.


val cats = listOf("Мурзик", "Барсик", "Рыжик")
println(cats[2])

Также можно использовать метод get() с указанием номера индекса.


val cats = listOf("Мурзик", "Барсик", "Рыжик")
println(cats.get(2))

Но у этих способов есть один недостаток - если вы укажете неправильное значение индекса, то получите исключение ArrayIndexOutOfBoundsException. Вы можете избежать проблемы, если будете использовать метод getOrElse() с указанием значения по умолчанию, если выйдете за пределы допустимых значений. В этом случае вам не придётся обрабатывать исключение.


val cats = listOf("Мурзик", "Барсик", "Рыжик")
println(cats.getOrElse(4) { "Неизвестный котик" })
// или как вариант, имя первого кота
println(cats.getOrElse(4) { cats.first() })

Другой вариант избежать исключения - использовать getOrNull(), который вернёт null при неправильном индексе. Осталось обработать ситуацию.


val cats = listOf("Мурзик", "Барсик", "Рыжик")
val cat = cats.getOrNull(4) ?: "Неизвестный котик"
println(cat)

Размер списка можно узнать через свойство size. Зная размер, можно без опаски пройтись по всем элементам списка.

Пройтись по всем элементам списка можно через for..in.


val cats = listOf("Cat1", "Cat2", "Cat3")
for(cat in cats) print (cat)

Умножаем каждый элемент списка на себя при помощи функции 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))

Функция flatMap() работает с коллекцией, содержащей коллекции, и возвращает объединённую «плоскую» коллекцию, содержащую все элементы исходных коллекций.


val result = listOf(listOf(1, 2, 3), listOf(4, 5, 6)).flatMap { it }
println(result) // [1, 2, 3, 4, 5, 6]

С помощью any и предиката можно узнать, выполняется ли условие хотя бы для одного элемента.


val list = listOf(1, 2, 3, 4, 5)
// есть ли элемент, который делится без остатка?
println(list.any { it % 2 == 0 }) // true
// есть ли элемент больше 10?
println(list.any { it > 10 }) // false

Выражение !any можно заменить на all.

С помощью all и предиката можно узнать, выполняется ли условие для всех элементов.


val list = listOf(1, 2, 3, 4, 5)
// все элементы меньше 7?
println(list.all { it < 7 }) // true

// всем котам меньше 11 лет?
val cats = listOf(Cat("Barsik", 5, true),
        Cat("Murzik", 9, true))
println(cats.all{it.age < 11}) // true

Выражение !all можно заменить на any.

С помощью none и предиката можно узнать, что ни один из всех элементов не выполняет условие.


val list = listOf(1, 2, 3, 4, 5)
// все элементы больше 6?
println(list.none { it > 6 }) // true

count с предикатом позволяет узнать число элементов в списке, которые соответствуют условию.


val list = listOf(1, 2, 3, 4, 5)
// сколько элементов меньше 4?
println(list.count { it < 4 }) // 3

// сколько котов младше 11 лет?
val cats = listOf(Cat("Barsik", 5),
        Cat("Murzik", 9))
println(cats.count{it.age < 11}) // 2

Не используйте для этой цели цепочку filter.size, так как вы создаёте промежуточную коллекцию, а это накладные расходы. Функция count считает только количество элементов, а не сами элементы.

fold задаёт начальное значение, а потом собирает все значения в списке от первого к последнему.


val list = listOf(1, 2, 3, 4, 5)

// 15 + 1 + 2 + 3 + 4 + 5
println(list.fold(15) { total, next -> total + next }) // 30

reduce работает аналогично, только без указания начального значения.


println(listOf(1, 2, 3).reduce { total, next ->
    total + next
})

foldRight работает аналогично fold, только значения берутся от последнего к первому.


println(listOf(1, 2, 3).foldRight(1) { total, next -> total * next })

reduceRight похож на foldRight без указания начального значения.

forEach позволяет пройтись по всем элементам списка.


listOf(1, 2, 3).forEach{println(it)}

forEachIndexed работает аналогично, вдобавок мы можем получить индекс позиции и его значение.


listOf("Барсик", "Рыжик", "Васька").forEachIndexed { index, value
    ->
    println("В позиции $index содержится $value")
}

В позиции 0 содержится Барсик
В позиции 1 содержится Рыжик
В позиции 2 содержится Васька

max() позволит получить максимальное значение из списка или null при отсутствии элементов. Работает с базовыми типами.


println(listOf(3, 9, 6).max()) // 9

Функция min() вычисляет минимальное значение из списка или null при отсутствии элементов. Работает с базовыми типами.

maxBy() возвращает первый элемент с наибольшим значением заданной функции или null, если элементов нет. Работает со всеми типами. Аналогичная функция minBy() возвращает первый элемент с наименьшим значением.

Выберем самого старшего кота.


val cats = listOf(Cat("Барсик", 5, 4),
        Cat("Мурзик", 9, 2))
println(cats.maxBy { it.age })
// println(cats.maxBy (Cat::age)) // альтернативный вариант

Функция maxWith() позволяет использовать Comparator. Выберем кота с самым длинным именем.


val cats = mutableListOf<Cat>()
cats.add(Cat("Мурзик", 9, 5400))
cats.add(Cat("Рыжик", 5, 6500))
cats.add(Cat("Василий", 4, 5100))

val maxLengthName:Cat? = cats.maxWith(
        Comparator{cat1, cat2 ->
            cat1.name.length - cat2.name.length
        }
)
maxLengthName?.apply {
    println("${this.name}:${this.age}:${this.weight}")
}

Кота с самым коротким именем можно вычислить через похожую функцию minWith().

sumBy() подсчитывает сумму всех элементов, после того, как они подверглись изменению.


// увеличиваем все элементы на 5, а потом складываем
println(listOf(1, 2, 3).sumBy { it + 5 }) // 21

// Считаем сумму длин строк
println(listOf("cat", "kitten").sumBy { it.length })

При работе с числами Double используйте функцию sumByDouble().

drop() отсекает указанное число элементов. Также есть функции dropWhile, dropLastWhile.


println(listOf(1, 2, 3, 4, 5).drop(2)) // [3, 4, 5]
println(listOf(1, 2, 3, 4, 5).dropWhile({ it < 3}))  // [3, 4, 5]
println(listOf(1, 2, 3, 4, 5).dropLastWhile { it > 4 })  // [1, 2, 3, 4]

take оставит первые элементы списка. takeLast оставит последние элементы.


// оставим первые два элемента
println(listOf(12, 32, 34, 45, 45).take(2)) // [12, 32]

// оставим последние два элемента
println(listOf(12, 32, 34, 45, 45).takeLast(2)) // [45, 45]

takeWhile оставит первые элементы, которые соответствуют условию.


println(listOf(1, 2, 3, 4, 5).takeWhile{it < 3}) // [1, 2]

// вернёт первые три символа
val chars = mutableListOf('c', 'a', 't', 'z', 'b', 'c', 'd', 'a', 'e', 'e', 'f', 'a')
val taken = chars.takeWhile { it < 'u' }
taken.forEach{
    println("$it")
}

Соответственно, takeLastWhile() оставит последние элементы, соответствующие условию.


val chars = mutableListOf('c', 'a', 't', 'z', 'b', 'c', 'd', 'a', 'e', 'e', 'f', 'a')
val taken = chars.takeLastWhile { it < 'e' } // вернёт последний символ 'a'
taken.forEach{
    println("$it")
}

Обратите внимание на разницу между takeWhile() и filter() (см. ниже). Первая функция будет отбирать элементы, пока выполняется условие и прервётся, а вторая пройдётся по всему списку до конца.


val chars = mutableListOf('c', 'a', 't', 'z', 'b', 'c', 'd', 'a', 'e', 'e', 'f', 'a')
var taken = chars.takeWhile { it < 'u' } // вернёт первые три символа, которые меньше символа 'u'
taken.forEach{
    println("takeWhile: $it")
}

taken = chars.filter { it < 'u' } // все символы, кроме 'z'
taken.forEach{
    println("filter: $it")
}

takeIf() будет выбирать элементы, если выполняется условие (предикат).


// Не выбирать элементы, если список содержит Пушистика
val cats = listOf("Рыжик", "Мурзик", "Барсик", "Васька")
cats.takeIf {
    it.contains("Пушистик")
}.apply {
    this?.forEach{
        println("$it")
    }
}

Обратная ситуация - выбирать элементы, если не выполняется условие (предикат).


// Выбрать элементы, если список не содержит Пушистика
val cats = listOf("Рыжик", "Мурзик", "Барсик", "Васька")
cats.takeUnless {
    it.contains("Пушистик")
}.apply {
    this?.forEach{
        println("$it")
    }
}

filter вернёт список, который соответствует предикату. Функция может удалять элементы из коллекции, но не может изменять их (используйте map).


// больше или равно 3
println(listOf(1, 2, 3, 4, 5).filter { it >= 3 }) // [3, 4, 5]

// чётные числа
println(listOf(1, 2, 3, 4, 5).filter { it % 2 == 0 }) // [2, 4]

val cats = listOf("Барсик", "Мурзик", "Васька", "Рыжик")
// слова, у которых второй символ "а"
println(cats.filter { it[1] == 'а' })
// [Барсик, Васька]

// Содержит "ик", сортируем по длине слова
val cats = listOf("Барсик", "Мурзик", "Пикассо", "Васька", "Рыжик")
val filtered = cats.filter { it.contains("ик") }.sortedBy { it.length }
println(filtered)

// Начинается на "П" и оканчивается на "к"
val cats = listOf("Барсик", "Мурзик", "Пикассо", "Васька", "Рыжик", "Пушок")
val filtered = cats.filter { it.startsWith('П') } .filter { it.endsWith('к') }
println(filtered) // Пушок

// Оставляем только толстых котов по весу
val cats = mutableListOf<Cat>()
cats.add(Cat("Murzik", 9, 5400))
cats.add(Cat("Barsik", 5, 6500))
cats.add(Cat("Vaska", 4, 5100))

val filteredCats = cats.filter { it.weight > 5200 }

filteredCats.forEach {
    println("Name: ${it.name}; Age: ${it.age}; Weight: ${it.weight}")
}

// толстые и молодые коты (срочно в фитнес-цетр!)
val filteredCats = cats.filter { it.age < 6 && it.weight > 5200 }

filterNot вернёт список, который не соответствует предикату.


// оставляем нечётные числа
println(listOf(1, 2, 3, 4, 5).filterNot { it % 2 == 0 }) // [1, 3, 5]

filterNotNull вернёт новый список, не содержащий null.


val cats = listOf("Мурзик", null, "Барсик", "Рыжик", null, "Васька", "Пушистик", null)
// Оставляем только котов
val filtered = cats.filterNotNull()
filtered.forEach {
    println("$it")
}

Расширенная версия filterNotNullTo() уберёт все элементы null и добавит оставшиеся элементы в новый список.


val cats = listOf("Мурзик", null, "Барсик", "Рыжик", null, "Васька", "Пушистик",
        null)

val allCats = mutableListOf("Мурка", "Милка")

cats.filterNotNullTo(allCats)

println(allCats.joinToString())

// Мурка, Милка, Мурзик, Барсик, Рыжик, Васька, Пушистик

contains() позволит убедиться, что нужный элемент присутствует в списке.


// содержится ли число 4 в списке
println(listOf(1, 2, 3, 4, 5).contains(4)) // true

// содержится ли в списке Барсик
val cats = listOf("Мурзик", "Барсик", "Рыжик")
println("Список содержит Барсика: ${cats.contains("Барсик")}")

Если нужно проверить сразу несколько элементов, то используйте containsAll(), передавая ему список желаемых элементов.


// содержится ли в списке Барсик и Мурзик
val cats = listOf("Мурзик", "Барсик", "Рыжик")
println("Барсик и Мурзик в списке: ${cats.containsAll(listOf("Барсик", "Мурзик"))}")

Получить элемент по индексу можно через elementAt(). Также доступны elementAtOrElse, elementAtOrNull.


println(listOf(1, 2, 3, 4, 5).elementAt(3)) // 4

first() вернёт первый элемент, соответствующий предикату. Если элемент не будет найдет, то получим исключение NoSuchElementException. Похожая функция firstOrNull вернёт null, если элемент не будет найден.


println(listOf(1, 2, 3, 4, 5).first{it % 3 == 0}) // 3

По такому же принципу работают last() и lastOrNull() для последнего элемента.

find похож на first.

indexOf() вернёт индекс элемента. Функции indexOfFirst(), indexOfLast, lastIndexOf() работают с предикатами.


println(listOf(1, 2, 3, 4, 5).indexOf(4)) // 3

Функция single() вернёт один уникальный элемент из списка. Если элементов, соответствующих условию, будет несколько будет исключение. singleOrNull вместо исключения вернёт null.


println(listOf(1, 6, 3, 4, 5).singleOrNull { it % 3 == 0 }) // null

Развернуть элементы списка можно через reverse() и reversed().


val list = mutableListOf(5, 9, 1)
list.reverse() // у изменяемого списка
println(list) // [ 1, 9, 5]

val list = listOf(1, 2, 3)
println(list.reversed()) //[3, 2, 1] // новый список, оригинальный остаётся без изменений

Сортировка: sort, sorted, sortedDescending, sortBy, sortByDescending, sortWith

Сортировка оригинального списка происходит через sort().


// используем изменяемый список
val list = mutableListOf(5, 9, 1)
list.sort()
println(list)

Отсортировать список можно через sorted(). Возвращается новый список, а оригинальный список остаётся без изменений.


val list = listOf(5, 9, 1)
println(list.sorted()) // [1, 5, 9]

В обратном порядке от большого к меньшему можно через sortedDescending(). Возвращается новый список, а оригинальный список остаётся без изменений.


val list = listOf(5, 9, 1)
println(list.sortedDescending())
// [9, 5, 1]

Сортировка по условию. Отсортируем по именам котов при помощи sortBy().


// класс Cat описан выше

val cats = mutableListOf<Cat>()
cats.add(Cat("Murzik", 9, 5400))
cats.add(Cat("Barsik", 5, 6500))
cats.add(Cat("Vaska", 4, 5100))

cats.sortBy { it.name }
cats.forEach {
    println("Name: ${it.name}; Age: ${it.age}; Weight: ${it.weight}")
}

Аналогично в обратном порядке через sortByDescending().

Если элементы содержат null, то можно сортировать при помощи sortWith() в связке с nullFirst().


// сначала null, потом остальные элементы по порядку
val cats = mutableListOf("cat", "murzik", null, "barsik", null, "kitten")
cats.sortWith(
        nullsFirst(compareBy{it})
)
cats.forEach{
    println("$it")
}

Обратная задача решается при помощи nullLast.


val cats = mutableListOf("cat", "murzik", null, "barsik", null, "kitten")
cats.sortWith(
        nullsLast(compareByDescending{it})
)
cats.forEach{
    println("$it")
}

Можно сортировать, сравнивая несколько полей класса. Порядок важен. Будем сортировать по имени и по возрасту (имена и возраст могут совпадать в списке).


val cats = mutableListOf<Cat>()
cats.add(Cat("Мурзик", 4, 5400))
cats.add(Cat("Рыжик", 5, 6500))
cats.add(Cat("Василий", 4, 5100))
cats.add(Cat("Мурзик", 6, 5400))

cats.sortWith(
        compareBy(
                {it.name}, {it.age}
        )
)

cats.forEach {
    println("${it.name}: ${it.age}, ${it.weight}")
}

Сортируем по длине имён в порядке возрастания, используя Comparator.


val cats = mutableListOf<Cat>()
cats.add(Cat("Мурзик", 4, 5400))
cats.add(Cat("Рыжик", 5, 6500))
cats.add(Cat("Мордотресь", 4, 5100))

cats.sortWith(
        Comparator { cat1, cat2 ->
            cat1.name.length - cat2.name.length
        }
)

cats.forEach {
    println("${it.name}: ${it.age}, ${it.weight}")
}

Если в порядке убывания, то поменяем местами аргументы.


cat2.name.length - cat1.name.length

Можно сложить два списка. К элементам первого списка будут добавлены элементы второго списка.


val list1 = listOf(1, 2, 3)
val bigList = list1 + listOf(4, 5)
println(bigList)
// [1, 2, 3, 4, 5]

Добавить ещё один элемент можно через plus(). Оригинальный список останется без изменений и будет создан новый список.


val list = listOf(1, 2, 3, 4, 5)
println(list.plus(6)) // новый список [1, 2, 3, 4, 5, 6]
println(list) // старый список [1, 2, 3, 4, 5]

Функция minus() отнимает указанный элемент.


val list = listOf(1, 2, 3, 4, 5)
println(list.minus(3)) // [1, 2, 4, 5]

Функция average() подсчитывает среднее значение всех элементов списка.


val list = listOf(1, 2, 3, 4, 5)
println(list.average()) // 3.0

Функция slice возвращает список по указанным индексам.


println(listOf(1, 2, 3, 4, 5).slice(listOf(1, 3, 4)))  // [2, 4, 5]

Функция shuffled() создаёт новый список с перемешанными элементами от старого списка.


val items = listOf(1, 2, 3)
val newList = items.shuffled()

println("shuffled: $newList") // shuffled: [2, 1, 3] один из вариантов

Функция shuffle() (Kotlin 1.2) перемешивает элементы изменяемого списка в случайном порядке.


val items = (1..5).toMutableList()

items.shuffle()
println("Shuffled items: $items")
// [2, 3, 1, 4, 5] один из вариантов

Разбить список на отдельные элементы с определённым условием можно через функцию partition. При значении true элементы попадают в одну группу, а остальные в другую. Возвращается объект Pair, содержащий два списка.


// по признаку чётности
val listA = listOf(1, 2, 3, 4, 5, 6)
val pair = listA.partition {
    it % 2 == 0
}
println(pair)
// ([2, 4, 6], [1, 3, 5])

Разделим котов на молодых и пожилых.


val cats = mutableListOf<Cat>()
cats.add(Cat("Мурзик", 4, 5400))
cats.add(Cat("Рыжик", 15, 6500))
cats.add(Cat("Барсик", 14, 5100))
cats.add(Cat("Васька", 7, 5400))

val (youngs, olds) = cats.partition { it.age < 10 }

println(youngs.joinToString())
println(olds.joinToString())

groupBy группирует значения по некоторому критерию. Например, мы хотим разбить список котов по возрасту.


val cats = listOf(Cat("Barsik", 5),
        Cat("Murzik", 9), Cat("Ryzhik", 5))
println(cats.groupBy { it.age })

// Результат
{5=[Cat(name=Barsik, age=5), Cat(name=Ryzhik, age=5)],
 9=[Cat(name=Murzik, age=9)]}

На выходе получается словарь с ключами (Map<Int, List<Cat>>), которые определяют признак группировки (в нашем случае это возраст). Мы получили одну группу котов с возрастом 5 лет и вторую группу с возрастом 9 лет. Вы можете изменить словарь при помощи mapKeys и mapValues.

Список может содержать дубликаты. Но если вы хотите избавиться от них, то можно преобразовать список во множество, а затем снова в список. Существует готовая функция distinct(), которая сделает это за вас.


val cats = listOf("Рыжик", "Мурзик", "Барсик", "Рыжик")
        .distinct()
println(cats)

Можно задать более точный критерий - например, ищем дубликаты по имени или по возрасту через distinctBy().


val cats = mutableListOf<Cat>()
cats.add(Cat("Мурзик", 4, 5400))
cats.add(Cat("Рыжик", 5, 6500))
cats.add(Cat("Барсик", 4, 5100))
cats.add(Cat("Рыжик", 7, 5400))

val distinctList: List<Cat> = cats.distinctBy { it.name } // либо it.age
println(distinctList.joinToString())

Можно даже создать сразу несколько условий для поиска дубликатов. Например, поищем дубликаты и по имени и по возрасту. Пусть у нас будет целая орава котов с одинаковыми именами. Некоторые из них могут быть одного возраста. Вес тоже может совпадать, но пока он нас не интересует.


val cats = mutableListOf<Cat>()
cats.add(Cat("Рыжик", 4, 5400))
cats.add(Cat("Рыжик", 5, 6500))
cats.add(Cat("Рыжик", 6, 5100))
cats.add(Cat("Рыжик", 5, 5400))

val distinctList: List<Cat> = cats.distinctBy { it.name to it.age }
println(distinctList.joinToString())

emptyList()

Пустой неизменяемый список можно создать через emptyList(), который вернёт тип List.


val emptyList: List<String> = emptyList<String>()

listOfNotNull()

Ещё один вид неизменяемых списков - listOfNotNull(). Вы можете поместить в список null, но они будут отсечены.


val cats = listOf("Мурзик", null, "Барсик", "Рыжик", null, "Васька", "Пушистик", null)
val notNullsCats = listOfNotNull("Мурзик", null, "Барсик", "Рыжик", null, "Васька", "Пушистик", null)

println(cats.joinToString()) // выводит все элементы, включая null
println(notNullsCats.joinToString()) // все элементы без null

// Мурзик, null, Барсик, Рыжик, null, Васька, Пушистик, null
// Мурзик, Барсик, Рыжик, Васька, Пушистик

arrayListOf()

Изменяемый список создаётся через arrayListOf(), который возвращает ArrayList.


//val stringList: ArrayList<String> = arrayListOf<String>("Hello", "Kitty")
val stringList = arrayListOf("Hello", "Kitty")
println(stringList::class.java) // class java.util.ArrayList

Сделаем обход коллекции с использованием индекса.


val list = arrayListOf("3", "8", "4", "1", "9")
for ((index, element) in list.withIndex()){
    println("$index: $element")
}

// Результат
0: 3
1: 8
2: 4
3: 1
4: 9

mutableListOf()

Изменять данные можно только в изменяемых списках. Но если у вас есть в наличии неизменяемый список, то его можно сконвертировать в изменяемый через специальный метод toMutableList(). При этом будет создан новый список.


var mutableNames = names.toMutableList() // превращаем в изменяемый список
mutableNames.add("Рыжик") // добавляем новое имя в конец списка
println(mutableNames::class.java)
println(mutableNames[3])

А можно сразу создать изменяемый список нужного типа через mutableListOf<T>(). Также есть перегруженная версия без указания нужного типа - mutableListOf().


val mutableListNames: MutableList<String> =
        mutableListOf<String>("Барсик", "Мурзик", "Васька")
mutableListNames.add("Рыжик") // добавляем в конец списка
mutableListNames.removeAt(1) // удаляем второй элемент
mutableListNames[0] = "Пушок" // заменяем первый элемент через присваивание
// mutableListNames.set(0, "Пушок") // другой вариант
mutableListNames.add(1, "Begemoth") // вставляем во вторую позицию

// изменяемый список из разных типов
val mutableListMixed = mutableListOf("Кот", "Собака", 5, 5.27, 'F')

При изменении списков индексы пересчитываются.

Удалить элемент из изменяемого списка можно через removeIf(), указав лямбда-выражение в качестве условия.


val cats = mutableListOf("Мурзик", "Барсик", "Рыжик")
cats.removeIf{it.contains("у")}
println(cats)

// выводится: [Барсик, Рыжик]

Удалить все элементы, которые подчиняются условию, можно также через removeAll():


val cats = mutableListOf("Мурзик", "Барсик", "Рыжик", "Васька")
// Удаляем элементы, которые начинаются на 'Б'
cats.removeAll { it.startsWith("Б") }

cats.forEachIndexed { index, name ->
    println("${index + 1}. $name")
}

//
1. Мурзик
2. Рыжик
3. Васька

Если надо не удалить, а оставить элементы по определённому условию, то используйте функцию retainAll().


val cats = mutableListOf("Мурзик", "Барсик", "Рыжик", "Васька")
// Оставляем элементы, которые заканчиваются на 'ик'
cats.retainAll { it.endsWith("ик") }

cats.forEachIndexed { index, name ->
    println("${index + 1}. $name")
}

Операции с двумя списками

С помощью addAll() можно добавить в список элементы другого списка.


var listA = mutableListOf("a", "a", "b")
var listB = mutableListOf("a", "c", "d")
listB.addAll(listA)
println(listB) // [a, c, d, a, a, b]

Другой способ - функция plusAssign():


var listA = mutableListOf("a", "a", "b")
var listB = mutableListOf("a", "c", "d")
listB.plusAssign(listA)
println(listB) // [a, c, d, a, a, b]

Вычитать элементы одного списка при помощи элементов другого списка можно через minusAssign().


var listA = mutableListOf("a", "a", "b", "u")
var listB = mutableListOf("a", "c", "d", "e", "u")
listB.minusAssign(listA)
println(listB) // [c, d, e]

Ещё один способ объединения двух списков с сохранением только уникальных элементов через union(). Обратите внимание на порядок сохранения - сначала берутся элементы основного списка, а затем добавляются элементы добавляемого списка.


val listA = mutableListOf("a", "e", "b")
val listB = mutableListOf("a", "c", "d", "e", "f")
val listC = listB.union(listA)
println(listC)

Функция-комбинатор zip() принимает разные коллекции и объединяют их в одну новую. Для примера объединим два списка: с именами котов и их размерами. Функция zip() возвращает новый список (коллекцию пар Pair). Для этой коллекции пар вызовем функцию toMap(), чтобы получить ассоциативный массив, к элементам которого можно обращаться по ключу. В этом случае ключ — имя кота.


val names = listOf("Барсик", "Мурзик", "Рыжик")
val sizes = listOf("большой", "средний", "совсем котёнок")
val catMap = names.zip(sizes).toMap()
// обращаемся к ключу
println(catMap["Рыжик"]) // совсем котёнок

replaceAll (Kotlin 1.2)

Заменяет каждый элемент списка новым значением в результате заданного выражения. Доступно для API 24 и выше


val items = (1..5).toMutableList()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    items.replaceAll { it * 2 }
}
println("Значения удвоены: $items") // [2, 4, 6, 8, 10]

fill (Kotlin 1.2)

Заменяет все элементы списка заданным значением.


val items = (1..5).toMutableList()

println("Было: $items")
items.fill(5)
println("Стало: $items")

//Было: [1, 2, 3, 4, 5]
//Стало: [5, 5, 5, 5, 5]

Все коллекция являются стандартными и полностью совместимыми с Java, Kotlin не использует собственных коллекций, но тем не менее предлагает удобные методы из коробки. Выше были показаны примеры для списков. Можно применять методы для других коллекций.

Например, найдём последний элемент из коллекции.


val set = setOf("Васька", "Мурзик", "Барсик")
println(set.last())

joinToString()

Встроенная котлиновская функция для создания строки из элементов коллекции. Вы можете задать собственный префикс и постфикс, а также разделитель. При большом количестве элементов можно задать лимит, тогда будут показаны первые элементы коллекции и после них многоточие.

Функция использует параметры по умолчанию, поэтому часть параметров можно опускать.


val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.joinToString()) // 1, 2, 3, 4, 5, 6
println(numbers.joinToString(prefix = "[", postfix = "]")) // [1, 2, 3, 4, 5, 6]
println(numbers.joinToString(prefix = "<", postfix = ">", separator = "•")) // <1•2•3•4•5•6>

val chars = charArrayOf('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q')
println(chars.joinToString(limit = 5, truncated = "...!") { it.toUpperCase().toString() }) // A, B, C, D, E, ...!

Создать список из случайных чисел

В Kotlin можно создать функцию для генерации списка из случайных чисел очень лаконичным способом.


private val random = Random()
private fun buildRandom() = List(25) { random.nextInt() }

// вызываем функцию
println(buildRandom())

ArrayDeque (Kotlin 1.3.70)

Новый класс.


val deque = ArrayDeque(listOf(1, 2, 3))

deque.addFirst(0)
deque.addLast(4)
println(deque) // [0, 1, 2, 3, 4]

println(deque.first()) // 0
println(deque.last()) // 4

deque.removeFirst()
deque.removeLast()
println(deque) // [1, 2, 3]
Реклама