Операции агрегирования коллекций
Коллекции Kotlin содержат функции для часто используемых агрегирующих операций – операций, возвращающих одно значение на основе содержимого коллекции. Большинство из них хорошо известны и работают так же, как и в других языках:
-
min()иmax()возвращают наименьший и наибольший элементы соответственно; -
average()возвращает среднее значение элементов в коллекции чисел; -
sum()возвращает сумму элементов в коллекции чисел; -
count()возвращает количество элементов в коллекции;
fun main() {
//sampleStart
val numbers = listOf(6, 42, 10, 4)
println("Count: ${numbers.count()}")
println("Max: ${numbers.max()}")
println("Min: ${numbers.min()}")
println("Average: ${numbers.average()}")
println("Sum: ${numbers.sum()}")
//sampleEnd
}
Также есть функции для получения наименьших и наибольших элементов по определённой функции-селектору или пользовательскому Comparator:
-
maxBy()/minBy()принимают функцию-селектор и возвращают элемент, для которого она возвращает наибольшее или наименьшее значение. -
maxWith()/minWith()принимают объектComparatorи возвращают наибольший или наименьший элемент в соответствии с этим объектомComparator.
fun main() {
//sampleStart
val numbers = listOf(5, 42, 10, 4)
val min3Remainder = numbers.minBy { it % 3 }
println(min3Remainder)
val strings = listOf("one", "two", "three", "four")
val longestString = strings.maxWith(compareBy { it.length })
println(longestString)
//sampleEnd
}
Кроме того, существуют расширенные функции суммирования, которые принимают функцию и возвращают сумму её возвращаемых значений для всех элементов:
-
sumBy()применяет функции, возвращающие значенияIntк элементам коллекции. -
sumByDouble()работает с функциями, возвращающими значенияDouble.
fun main() {
//sampleStart
val numbers = listOf(5, 42, 10, 4)
println(numbers.sumBy { it * 2 })
println(numbers.sumByDouble { it.toDouble() / 2 })
//sampleEnd
}
Свертка и редукция
Для более специфических случаев существуют функции reduce() и fold(), которые последовательно применяют заданную операцию к элементам коллекции и возвращают накопленный результат. Операция принимает два аргумента: ранее накопленное значение и элемент коллекции.
Разница между двумя функциями заключается в том, что fold() принимает начальное значение и использует его как накопленное значение на первом шаге, в то время как на первом шаге reduce() использует первый и второй элементы как аргументы операции.
fun main() {
//sampleStart
val numbers = listOf(5, 2, 10, 4)
val sum = numbers.reduce { sum, element -> sum + element }
println(sum)
val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 }
println(sumDoubled)
//val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 } //incorrect: the first element isn't doubled in the result
//println(sumDoubledReduce)
//sampleEnd
}
Приведённый выше пример демонстрирует разницу: fold() используется для вычисления суммы удвоенных элементов. Если вы передадите ту же функцию в reduce(), она вернёт другой результат, потому что на первом шаге она использует первый и второй элементы списка как аргументы, поэтому первый элемент не будет удвоен.
Для применения функции к элементам в обратном порядке используйте функции reduceRight() и foldRight(). Они работают аналогично fold() и reduce() , но начинаются с последнего элемента и затем продолжают к предыдущим. Обратите внимание, что при правой свёрты или редукции аргументы операции меняют свой порядок: сначала идёт элемент, а затем накопленное значение.
fun main() {
//sampleStart
val numbers = listOf(5, 2, 10, 4)
val sumDoubledRight = numbers.foldRight(0) { element, sum -> sum + element * 2 }
println(sumDoubledRight)
//sampleEnd
}
Вы также можете применять операции, которые принимают индексы элементов в качестве параметров. Для этой цели используйте функции reduceIndexed() и foldIndexed(), передавая индекс элемента как первый аргумент операции.
Наконец, существуют функции, которые применяют такие операции к элементам коллекции справа налево — reduceRightIndexed() и foldRightIndexed().
fun main() {
//sampleStart
val numbers = listOf(5, 2, 10, 4)
val sumEven = numbers.foldIndexed(0) { idx, sum, element -> if (idx % 2 == 0) sum + element else sum }
println(sumEven)
val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum -> if (idx % 2 == 0) sum + element else sum }
println(sumEvenRight)
//sampleEnd
}
Все операции редукции выбрасывают исключение при пустых коллекциях. Чтобы получить null вместо этого, используйте их *OrNull() аналоги:
© 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-aggregate.html