Spec-Zone.ru › Kotlin 1.8

Операции преобразования коллекций

Стандартная библиотека Kotlin предоставляет набор расширяющих функций для преобразования коллекций. Эти функции создают новые коллекции из существующих на основе заданных правил преобразования. На этой странице мы рассмотрим доступные функции преобразования коллекций.

Преобразование Map

Преобразование отображения создаёт коллекцию из результатов применения функции к элементам другой коллекции. Основная функция отображения — map(). Она применяет заданную лямбда-функцию к каждому элементу и возвращает список результатов лямбды. Порядок результатов такой же, как и в исходном порядке элементов. Для применения преобразования, которое дополнительно использует индекс элемента в качестве аргумента, используйте mapIndexed().

fun main() {
//sampleStart
    val numbers = setOf(1, 2, 3)
    println(numbers.map { it * 3 })
    println(numbers.mapIndexed { idx, value -> value * idx })
//sampleEnd
}

Если преобразование генерирует null для определённых элементов, вы можете отфильтровать null из результирующей коллекции, вызвав функцию mapNotNull() вместо map(), или mapIndexedNotNull() вместо mapIndexed().

fun main() {
//sampleStart
    val numbers = setOf(1, 2, 3)
    println(numbers.mapNotNull { if ( it == 2) null else it * 3 })
    println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })
//sampleEnd
}

При преобразовании карт у вас есть два варианта: преобразовать ключи, оставив значения неизменными, и наоборот. Для применения заданного преобразования к ключам используйте mapKeys(); в свою очередь, mapValues() преобразует значения. Обе функции используют преобразования, которые принимают пару «ключ-значение» в качестве аргумента, поэтому вы можете работать как с ключом, так и со значением.

fun main() {
//sampleStart
    val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
    println(numbersMap.mapKeys { it.key.uppercase() })
    println(numbersMap.mapValues { it.value + it.key.length })
//sampleEnd
}

Слияние (Zip)

Преобразование слияния создаёт пары из элементов с одинаковыми позициями в обеих коллекциях. В стандартной библиотеке Kotlin это выполняется с помощью расширяющей функции zip().

Когда она вызывается для коллекции или массива с другой коллекцией (или массивом) в качестве аргумента, zip() возвращает List объектов Pair. Элементы коллекции-приёмника являются первыми элементами в этих парах.

Если коллекции имеют разные размеры, размер результата zip() равен меньшему размеру; последние элементы большей коллекции не включаются в результат.

zip() также может быть вызвана в инфиксной форме a zip b.

fun main() {
//sampleStart
    val colors = listOf("red", "brown", "grey")
    val animals = listOf("fox", "bear", "wolf")
    println(colors zip animals)

    val twoAnimals = listOf("fox", "bear")
    println(colors.zip(twoAnimals))
//sampleEnd
}

Вы также можете вызвать zip() с функцией преобразования, которая принимает два параметра: элемент-приёмник и элемент-аргумент. В этом случае результат List содержит возвращаемые значения функции преобразования, вызываемой для пар элементов-приёмника и элементов-аргументов с одинаковыми позициями.

fun main() {
//sampleStart
    val colors = listOf("red", "brown", "grey")
    val animals = listOf("fox", "bear", "wolf")
    
    println(colors.zip(animals) { color, animal -> "The ${animal.replaceFirstChar { it.uppercase() }} is $color"})
//sampleEnd
}

Если у вас есть List пар Pair, вы можете выполнить обратное преобразование — разделение — которое создаёт две списка из этих пар:

  • Первый список содержит первые элементы каждой Pair в исходном списке.

  • Второй список содержит вторые элементы.

Чтобы разделить список пар, вызовите unzip().

fun main() {
//sampleStart
    val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
    println(numberPairs.unzip())
//sampleEnd
}

Сопоставление (Associate)

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

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

fun main() {
//sampleStart
    val numbers = listOf("one", "two", "three", "four")
    println(numbers.associateWith { it.length })
//sampleEnd
}

Для создания карт, в которых элементы коллекции являются значениями, существует функция associateBy(). Она принимает функцию, которая возвращает ключ на основе значения элемента. Если ключи двух элементов равны, в карте остаётся только последний.

associateBy() также может быть вызвана с функцией преобразования значений.

fun main() {
//sampleStart
    val numbers = listOf("one", "two", "three", "four")

    println(numbers.associateBy { it.first().uppercaseChar() })
    println(numbers.associateBy(keySelector = { it.first().uppercaseChar() }, valueTransform = { it.length }))
//sampleEnd
}

Ещё один способ создания карт, в которых и ключи, и значения каким-то образом производятся из элементов коллекции, — функция associate(). Она принимает лямбда-функцию, которая возвращает пару «ключ-значение» соответствующей записи карты.

Обратите внимание, что associate() создаёт объекты с коротким сроком жизни Pair, что может повлиять на производительность. Поэтому associate() следует использовать, когда производительность не критически важна или это предпочтительнее других вариантов.

Примером последнего случая является ситуация, когда ключ и соответствующее значение производятся из элемента одновременно.

fun main() {
data class FullName (val firstName: String, val lastName: String)

fun parseFullName(fullName: String): FullName {
    val nameParts = fullName.split(" ")
    if (nameParts.size == 2) {
        return FullName(nameParts[0], nameParts[1])
    } else throw Exception("Wrong name format")
}

//sampleStart
    val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
    println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } })  
//sampleEnd
}

Здесь мы сначала вызываем функцию преобразования для элемента, а затем создаём пару из свойств результата этой функции.

Разворачивание (Flatten)

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

Первая функция — flatten(). Вы можете вызвать её для коллекции коллекций, например, для List из Set . Функция возвращает одну коллекцию всех элементов вложенных коллекций.

fun main() {
//sampleStart
    val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
    println(numberSets.flatten())
//sampleEnd
}

Другая функция — flatMap() — предоставляет гибкий способ обработки вложенных коллекций. Она принимает функцию, которая отображает элемент коллекции в другую коллекцию. В результате flatMap() возвращает один список её возвращаемых значений для всех элементов. Таким образом, flatMap() работает как последовательное применение map() (с коллекцией в качестве результата отображения) и flatten().

data class StringContainer(val values: List<String>)

fun main() {
//sampleStart
    val containers = listOf(
        StringContainer(listOf("one", "two", "three")),
        StringContainer(listOf("four", "five", "six")),
        StringContainer(listOf("seven", "eight"))
    )
    println(containers.flatMap { it.values })
//sampleEnd
}

Представление в виде строки

Если вам нужно получить содержимое коллекции в удобочитаемом формате, используйте функции, которые преобразуют коллекции в строки: joinToString() и joinTo().

joinToString() строит одну строку из элементов коллекции на основе предоставленных аргументов. joinTo() делает то же самое, но добавляет результат к заданному объекту Appendable.

При вызове с аргументами по умолчанию функции возвращают результат, аналогичный вызову toString() для коллекции: строку с представлениями элементов в виде строк, разделёнными запятыми и пробелами.

fun main() {
//sampleStart
    val numbers = listOf("one", "two", "three", "four")
    
    println(numbers)         
    println(numbers.joinToString())
    
    val listString = StringBuffer("The list of numbers: ")
    numbers.joinTo(listString)
    println(listString)
//sampleEnd
}

Для построения настраиваемого представления в строке вы можете указать её параметры в аргументах функций separator, prefix, и postfix. Результирующая строка начнётся с prefix и закончится postfix. separator будет добавляться после каждого элемента, кроме последнего.

fun main() {
//sampleStart
    val numbers = listOf("one", "two", "three", "four")    
    println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
//sampleEnd
}

Для больших коллекций вы можете указать limit — количество элементов, которые будут включены в результат. Если размер коллекции превышает limit, все остальные элементы будут заменены единственным значением аргумента truncated.

fun main() {
//sampleStart
    val numbers = (1..100).toList()
    println(numbers.joinToString(limit = 10, truncated = "<...>"))
//sampleEnd
}

Наконец, для настройки представления самих элементов укажите функцию transform.

fun main() {
//sampleStart
    val numbers = listOf("one", "two", "three", "four")
    println(numbers.joinToString { "Element: ${it.uppercase()}"})
//sampleEnd
}
Последнее изменение: 10 января 2023
Обзор операций с коллекциями Фильтрация коллекций

© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/collection-transformations.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API