Найти в Дзене

Обзор Kotlin Collections API

Оглавление

1. Обзор

"В этом быстром руководстве мы представим Collections API в Kotlin и обсудим различные типы коллекций в Kotlin, а также некоторые общие операции над коллекциями.

2. Коллекция против Изменяемой коллекции

Сначала давайте рассмотрим различные типы коллекций в Kotlin. Мы увидим, как инициализировать основные типы коллекций.

Интерфейс Collection поддерживает только методы для чтения, в то время как MutableCollection поддерживает методы для чтения и записи.

2.1 List

Мы можем создать простой список только для чтения с использованием метода listOf() и список для чтения и записи с использованием mutableListOf():

val theList = listOf("one", "two", "three")

val theMutableList = mutableListOf("one", "two", "three")

2.2 Set

Аналогично мы можем создать множество только для чтения с использованием метода setOf() и множество для чтения и записи с использованием mutableSetOf():

val theSet = setOf("one", "two", "three")

val theMutableSet = mutableSetOf("one", "two", "three")

2.3 Map

Мы также можем создать отображение только для чтения с использованием метода mapOf() и отображение для чтения и записи с использованием mutableMapOf():

val theMap = mapOf(1 to "one", 2 to "two", 3 to "three")

val theMutableMap = mutableMapOf(1 to "one", 2 to "two", 3 to "three")

3. Полезные операторы

Коллекционное API в Kotlin намного богаче, чем то, что можно найти в Java – оно поставляется с набором перегруженных операторов.

3.1 Оператор "in"

Мы можем использовать выражение 'x in collection', что можно перевести как 'collection.contains(x)':

@Test
fun whenSearchForExistingItem_thenFound () {
val theList = listOf("one", "two", "three")

assertTrue("two" in theList)
}

3.2 Оператор "+"

Мы можем добавить элемент или целую коллекцию к другой с использованием оператора '+' (плюс):

@Test
fun whenJoinTwoCollections_thenSuccess () {
val firstList = listOf("one", "two", "three")
val secondList = listOf("four", "five", "six")
val resultList = firstList + secondList

assertEquals(6, resultList.size)
assertTrue(resultList.contains("two"))
assertTrue(resultList.contains("five"))
}

3.3 Оператор "-"

Точно так же мы можем удалить элемент или несколько элементов с использованием оператора '-' (минус):

@Test
fun whenExcludeItems_thenRemoved () {
val firstList = listOf("one", "two", "three")
val secondList = listOf("one", "three")
val resultList = firstList - secondList

assertEquals(1, resultList.size)
assertTrue(resultList.contains("two"))
}

4. Другие методы

Наконец, мы рассмотрим некоторые общие методы для работы с коллекциями. В Java, чтобы использовать более сложные методы, нам нужно было бы использовать Stream API.

В Kotlin мы можем обнаружить аналогичные методы в API коллекций.

4.1 Slicing

Мы можем получить подсписок из заданного списка:

@Test
fun whenSliceCollection_thenSuccess () {
val theList = listOf("one", "two", "three")
val resultList = theList.slice(1..2)

assertEquals(2, resultList.size)
assertTrue(resultList.contains("two"))
}

4.2 Removing

Мы можем получить подсписок из заданного списка:

@Test
fun whenFilterNullValues_thenSuccess () {
val theList = listOf("one", null, "two", null, "three")
val resultList = theList.filterNotNull()

assertEquals(3, resultList.size)
}

4.3 Filtering

Мы можем получить подсписок из заданного списка:

@Test
fun whenFilterNonPositiveValues_thenSuccess () {
val theList = listOf(1, 2, -3, -4, 5, -6)
val resultList = theList.filter{ it > 0}

assertEquals(3, resultList.size)
assertTrue(resultList.contains(1))
assertFalse(resultList.contains(-4))
}

4.4 Dropping

Мы можем удалить первые N элементов:

@Test
fun whenDropFirstItems_thenRemoved () {
val theList = listOf("one", "two", "three", "four")
val resultList = theList.drop(2)

assertEquals(2, resultList.size)
assertFalse(resultList.contains("one"))
assertFalse(resultList.contains("two"))
}

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

@Test
fun whenDropFirstItemsBasedOnCondition_thenRemoved () {
val theList = listOf("one", "two", "three", "four")
val resultList = theList.dropWhile{ it.length < 4 }

assertEquals(2, resultList.size)
assertFalse(resultList.contains("one"))
assertFalse(resultList.contains("two"))
}

4.5 Grouping

Мы можем группировать элементы:

@Test
fun whenGroupItems_thenSuccess () {
val theList = listOf(1, 2, 3, 4, 5, 6)
val resultMap = theList.groupBy{ it % 3}

assertEquals(3, resultMap.size)

assertTrue(resultMap[1]!!.contains(1))
assertTrue(resultMap[2]!!.contains(5))
}

4.6 Mapping

"Мы можем отобразить все элементы, используя предоставленную функцию:

@Test
fun whenApplyFunctionToAllItems_thenSuccess () {
val theList = listOf(1, 2, 3, 4, 5, 6)
val resultList = theList.map{ it * it }

assertEquals(4, resultList[1])
assertEquals(9, resultList[2])
}

Мы можем использовать flatMap() для выравнивания вложенных коллекций. Здесь мы преобразуем строки в List<String>, избегая создания List<List<String>>:

@Test
fun whenApplyMultiOutputFunctionToAllItems_thenSuccess () {
val theList = listOf("John", "Tom")
val resultList = theList.flatMap{ it.toLowerCase().toList() }

assertEquals(7, resultList.size)
}

4.7 Reduction

Мы можем выполнять операцию свертки/уменьшения (fold/reduce):

@Test
fun whenApplyFunctionToAllItemsWithStartingValue_thenSuccess () {
val theList = listOf(1, 2, 3, 4, 5, 6)
val finalResult = theList.fold(0, {acc, i -> acc + (i * i)})

assertEquals(91, finalResult)
}

4.8 Chunking

Чтобы разбить коллекцию на части заданного размера, мы можем использовать метод chunked():

@Test
fun whenApplyingChunked_thenShouldBreakTheCollection() {
val theList = listOf(1, 2, 3, 4, 5)
val chunked = theList.chunked(2)

assertThat(chunked.size).isEqualTo(3)
assertThat(chunked.first()).contains(1, 2)
assertThat(chunked[1]).contains(3, 4)
assertThat(chunked.last()).contains(5)
}

Поскольку в коллекции пять элементов, вызов метода chunked(2) возвращает две коллекции по два элемента и одну коллекцию с одним элементом.

Также можно отобразить каждый фрагмент на что-то другое после разбиения коллекции:

@Test
fun whenApplyingChunkedWithTransformation_thenShouldBreakTheCollection() {
val theList = listOf(1, 2, 3, 4, 5)
val chunked = theList.chunked(3) { it.joinToString(", ") }

assertThat(chunked.size).isEqualTo(2)
assertThat(chunked.first()).isEqualTo("1, 2, 3")
assertThat(chunked.last()).isEqualTo("4, 5")
}

После создания фрагментов размером 3, мы преобразуем каждый фрагмент в строку, разделенную запятыми.

4.9 Windowing

Функция windowed() возвращает список диапазонов элементов, перемещая скользящее окно заданного размера по коллекции элементов.

Чтобы лучше понять это, давайте посмотрим, как работает windowed(3) на коллекции из 6 элементов:

-2

Сначала размер окна равен 3, поэтому первый список будет содержать 1, 2 и 3. Затем скользящее окно перемещается на один элемент вперед:

-3

Скользящее окно перемещается вперед, пока не удастся создать еще один список заданного размера:

-4

Эта последовательность переходов проявляется в коде Kotlin следующим образом:

@Test
fun whenApplyingWindowed_thenShouldCreateSlidingWindowsOfElements() {
val theList = (1..6).toList()
val windowed = theList.windowed(3)

assertThat(windowed.size).isEqualTo(4)
assertThat(windowed.first()).contains(1, 2, 3)
assertThat(windowed[1]).contains(2, 3, 4)
assertThat(windowed[2]).contains(3, 4, 5)
assertThat(windowed.last()).contains(4, 5, 6)
}

По умолчанию скользящее окно перемещается на один шаг вперед каждый раз. Конечно, мы можем изменить это, передав пользовательское значение шага:

@Test
fun whenApplyingWindowedWithTwoSteps_thenShouldCreateSlidingWindowsOfElements() {
val theList = (1..6).toList()
val windowed = theList.windowed(size = 3, step = 2)

assertThat(windowed.size).isEqualTo(2)
assertThat(windowed.first()).contains(1, 2, 3)
assertThat(windowed.last()).contains(3, 4, 5)
}

Функция windowed(), по умолчанию, всегда и только создает диапазоны заданного размера. Чтобы изменить это, мы можем установить параметр partialWindows в true:

@Test
fun whenApplyingPartialWindowedWithTwoSteps_thenShouldCreateSlidingWindowsOfElements() {
val theList = (1..6).toList()
val windowed = theList.windowed(size = 3, step = 2, partialWindows = true)

assertThat(windowed.size).isEqualTo(3)
assertThat(windowed.first()).contains(1, 2, 3)
assertThat(windowed[1]).contains(3, 4, 5)
assertThat(windowed.last()).contains(5, 6)
}

Аналогично функции chunked(), можно отобразить каждый диапазон на что-то другое:

@Test
fun whenApplyingTransformingWindows_thenShouldCreateSlidingWindowsOfElements() {
val theList = (1..6).toList()
val windowed = theList.windowed(size = 3, step = 2, partialWindows = true) { it.joinToString(", ") }

assertThat(windowed.size).isEqualTo(3)
assertThat(windowed.first()).isEqualTo("1, 2, 3")
assertThat(windowed[1]).isEqualTo("3, 4, 5")
assertThat(windowed.last()).isEqualTo("5, 6")
}

5. Заключение

Мы изучили Collections API в Kotlin и некоторые из наиболее интересных методов.

Оригинал статьи: https://www.baeldung.com/kotlin/collections-api