Агрегирующие операции
Коллекции Kotlin содержат функции для часто используемых агрегирующих операций — операций, которые возвращают одно значение на основе содержимого коллекции. Большинство из них хорошо известны и работают так же, как и в других языках:
minOrNull()иmaxOrNull()возвращают соответственно наименьший и наибольший элементы. Для пустых коллекций они возвращаютnull.average()возвращает среднее значение элементов числовой коллекции.sum()возвращает сумму элементов числовой коллекции.count()возвращает количество элементов в коллекции.
fun main() {
val numbers = listOf(6, 42, 10, 4)
println("Count: ${numbers.count()}")
println("Max: ${numbers.maxOrNull()}")
println("Min: ${numbers.minOrNull()}")
println("Average: ${numbers.average()}")
println("Sum: ${numbers.sum()}")
}
Существуют также функции для получения наименьших и наибольших элементов с помощью определённой функции-селектора или пользовательского Comparator:
maxByOrNull()иminByOrNull()принимают функцию-селектор и возвращают элемент, для которого она возвращает наибольшее или наименьшее значение.maxWithOrNull()иminWithOrNull()принимают объектComparatorи возвращают наибольший или наименьший элемент согласно этомуComparator.maxOfOrNull()иminOfOrNull()принимают функцию-селектор и возвращают наибольшее или наименьшее значение, возвращённое самой функцией-селектором.maxOfWithOrNull()иminOfWithOrNull()принимают объектComparatorи возвращают наибольшее или наименьшее значение, возвращённое селектором, согласно этомуComparator.
Эти функции возвращают null для пустых коллекций. Есть и альтернативы — maxOf, minOf, maxOfWith и minOfWith — они работают так же, как соответствующие им функции, но выбрасывают NoSuchElementException для пустых коллекций.
fun main() {
//sampleStart
val numbers = listOf(5, 42, 10, 4)
val min3Remainder = numbers.minByOrNull { it % 3 }
println(min3Remainder)
val strings = listOf("one", "two", "three", "four")
val longestString = strings.maxWithOrNull(compareBy { it.length })
println(longestString)
//sampleEnd
}
Помимо обычного sum(), существует расширенная функция суммирования sumOf(), которая принимает функцию-селектор и возвращает сумму результатов её применения ко всем элементам коллекции. Функция-селектор может возвращать значения разных числовых типов: Int, Long, Double, UInt и ULong (а также BigInteger и BigDecimal на JVM).
fun main() {
//sampleStart
val numbers = listOf(5, 42, 10, 4)
println(numbers.sumOf { it * 2 })
println(numbers.sumOf { it.toDouble() / 2 })
//sampleEnd
}
Свёртка и редукция
Для более специфичных случаев существуют функции reduce() и fold(), которые последовательно применяют заданную операцию к элементам коллекции и возвращают накопленный результат. Операция принимает два аргумента: ранее накопленное значение и элемент коллекции.
Разница между этими двумя функциями заключается в том, что fold() принимает начальное значение и использует его в качестве накопленного значения на первом шаге, тогда как на первом шаге reduce() использует в качестве аргументов операции первый и второй элементы.
fun main() {
//sampleStart
val numbers = listOf(5, 2, 10, 4)
val simpleSum = numbers.reduce { sum, element -> sum + element }
println(simpleSum)
val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 }
println(sumDoubled)
//incorrect: the first element isn't doubled in the result
//val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 }
//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():
Если необходимо сохранять промежуточные значения аккумулятора, используйте функции runningFold() (или её синоним scan()) и runningReduce().
fun main() {
//sampleStart
val numbers = listOf(0, 1, 2, 3, 4, 5)
val runningReduceSum = numbers.runningReduce { sum, item -> sum + item }
val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item }
//sampleEnd
val transform = { index: Int, element: Int -> "N = ${index + 1}: $element" }
println(runningReduceSum.mapIndexed(transform).joinToString("\n", "Sum of first N elements with runningReduce:\n"))
println(runningFoldSum.mapIndexed(transform).joinToString("\n", "Sum of first N elements with runningFold:\n"))
}
Если в параметре операции нужен индекс, используйте runningFoldIndexed() или runningReduceIndexed().
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/collection-aggregate.html