Операции коллекций: общий обзор
Стандартная библиотека Kotlin предлагает большой набор функций для работы с коллекциями. Сюда входят простые операции, такие как получение или добавление элементов, а также более сложные, например, поиск, сортировка, фильтрация, преобразование и т. д.
Функции-расширения (Extension) и функции-члены (member functions)
Операции коллекций в стандартной библиотеке объявляются двумя способами: как функции-члены и как функции-расширения.
С помощью функций-членов определяются операции, которые необходимы для определённых типов коллекций. Например, у
Collection
есть функция
isEmpty()
для проверки того, что коллекция пуста; у
List
есть функция
get()
для обращения к элементу по его индексу и т. д.
Если вы решите создать свою собственную реализацию коллекции, например, на основе интерфейса Collection
, то вы также должны будете реализовать все его функции-члены. Чтобы упростить эту задачу, используйте специально подготовленные реализации интерфейсов из стандартной библиотеки:
AbstractCollection
,
AbstractList
,
AbstractSet
,
AbstractMap
.
Помимо вышеперечисленных есть аналоги для создания изменяемых (mutable) коллекций.
Другие операции коллекций объявлены как функции-расширения. К ним относятся операции для фильтрации, преобразования, сортировки и прочих видов обработки элементов коллекции.
Общие операции
Общие операции доступны как для изменяемых, так и для неизменяемых коллекций. Их можно разделить на следующие группы:
- Преобразование
- Фильтрация
- Операторы
plus
иminus
- Группировка
- Выборка элементов
- Выбор одного элемента
- Сортировка
- Объединение
Операции, описанные в разделах выше, возвращают результат, не изменяя исходную коллекцию. Например, операция filter
создаёт новую коллекцию, которая содержит элементы, соответствующие условию фильтра.
Результаты этих операций можно использовать несколькими способами, например, сохранять в переменные или передавать в другие функции.
fun main() {
val numbers = listOf("one", "two", "three", "four")
numbers.filter { it.length > 3 } // список `numbers` не изменится, результат фильтра теряется
println("numbers are still $numbers") // [one, two, three, four]
val longerThan3 = numbers.filter { it.length > 3 } // результат сохраняется в `longerThan3`
println("numbers longer than 3 chars are $longerThan3") // [three, four]
}
Некоторые функции в качестве параметра принимают целевой объект - destination. Он представляет собой изменяемую коллекцию, в которую функция добавляет результаты своих вычислений, вместо того, чтобы самостоятельно создавать новый объект.
Названия таких функций можно отличить по постфиксу To
, например,
filterTo()
вместо
filter()
или
associateTo()
вместо
associate()
.
fun main() {
val numbers = listOf("one", "two", "three", "four")
val filterResults = mutableListOf<String>() // целевой объект
numbers.filterTo(filterResults) { it.length > 3 }
numbers.filterIndexedTo(filterResults) { index, _ -> index == 0 }
println(filterResults) // содержит результат обеих операций - [three, four, one]
}
Эти функции возвращают целевой объект обратно, поэтому его можно создать прямо при вызове функции:
fun main() {
val numbers = listOf("one", "two", "three", "four")
// отфильтрованные элементы будут помещены в `HashSet`,
// который автоматом исключит дубликаты
val result = numbers.mapTo(HashSet()) { it.length }
println("distinct item lengths are $result") // [3, 4, 5]
}
Почти все стандартные функции имеют аналог в виде функции, работающей с целевым объектом. С полным списком можно ознакомиться в документации.
Операции записи
Для изменяемых коллекций доступны так называемые операции записи, которые изменяют состояние коллекции. Это операции по добавлению, удалению и обновлению элементов. Подробнее они рассматриваются в отдельном разделе - Операции записи, а также частично в разделах List: специфичные операции и Map: специфичные операции.
Помимо прочего существуют пары функций, которые выполняют одинаковые операции, но с одной лишь разницей: одна функция изменяет исходную коллекцию и поэтому работает только с изменяемыми типами, тогда как вторая возвращает результат в виде новой коллекции и таким образом позволяет работать с изменяемыми и неизменяемыми типами коллекций.
Например, функция sort()
сортирует исходную изменяемую коллекцию, тем самым меняя её состояние;
функция sorted()
создаёт новую коллекцию, содержащую те же элементы, что и исходная, но в отсортированном порядке.
fun main() {
val numbers = mutableListOf("one", "two", "three", "four")
val sortedNumbers = numbers.sorted()
println(numbers == sortedNumbers) // false
numbers.sort()
println(numbers == sortedNumbers) // true
}