Spec-Zone.ru › Kotlin 1.4

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

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

Картирование

Преобразование картирования создаёт коллекцию из результатов применения функции к элементам другой коллекции. Основная функция для картирования — 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.toUpperCase() })
    println(numbersMap.mapValues { it.value + it.key.length })
//sampleEnd
}

Сцепление

Преобразование сцепления создаёт пары из элементов с одинаковыми позициями в обеих коллекциях. В стандартной библиотеке 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.capitalize()} 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
}

Ассоциация

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

Основная функция ассоциации 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().toUpperCase() })
    println(numbers.associateBy(keySelector = { it.first().toUpperCase() }, 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(). Вы можете вызвать её на коллекции коллекций, например, на List Set. Функция возвращает единую List всех элементов вложенных коллекций.

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() строит одно String из элементов коллекции на основе предоставленных аргументов. 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.toUpperCase()}"})
//sampleEnd
}

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

Spec-Zone.ru

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