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 элементов:
Сначала размер окна равен 3, поэтому первый список будет содержать 1, 2 и 3. Затем скользящее окно перемещается на один элемент вперед:
Скользящее окно перемещается вперед, пока не удастся создать еще один список заданного размера:
Эта последовательность переходов проявляется в коде 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