9.1.1.9.2 聚合操作
原文链接: https://kotlinlang.org/docs/collection-aggregate.html
9.1.1.9.2 聚合操作
Kotlin 集合包含用于常见聚合操作的函数——聚合操作是只根据集合内容返回单个值的操作。其中大多数都很常见,并且行为与其他语言中相同:
1
2
3
4
5
6
7
8
9
10
|
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 获取最小和最大的元素:
这些函数对空集合返回 null。也有替代版本——maxOf、minOf、maxOfWith 和 minOfWith——它们的作用与对应版本相同,但对空集合会抛出 NoSuchElementException。
1
2
3
4
5
6
7
8
9
10
|
fun main() {
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)
}
|
除了常规的 sum() 之外,还有一个高级求和函数 sumOf(),它接收一个选择器函数,并返回把它应用于所有集合元素后的总和。选择器可以返回不同的数值类型:Int、Long、Double、UInt 和 ULong(在 JVM 上还有 BigInteger 和 BigDecimal)。
1
2
3
4
5
6
|
fun main() {
val numbers = listOf(5, 42, 10, 4)
println(numbers.sumOf { it * 2 })
println(numbers.sumOf { it.toDouble() / 2 })
}
|
Fold 与 reduce
针对更具体的场景,还有 reduce() 和 fold() 函数,它们把提供的操作依次应用于集合元素并返回累积结果。该操作接收两个参数:此前累积的值和当前集合元素。
这两个函数的区别在于,fold() 接收一个初始值并把它在第一步用作累积值,而 reduce() 的第一步则把第一个和第二个元素用作操作参数。
1
2
3
4
5
6
7
8
9
10
11
12
| fun main() {
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)
// 不正确:结果中第一个元素没有被加倍
// val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 }
// println(sumDoubledReduce)
}
|
上面的例子展示了这种差异:fold() 用于计算各元素两倍后的总和。如果你把同样的函数传给 reduce(),它会返回另一个结果,因为它在第一步使用列表的第一个和第二个元素作为参数,所以第一个元素不会被加倍。
要按相反顺序对元素应用函数,请使用 reduceRight() 和 foldRight() 函数。它们的工作方式与 fold() 和 reduce() 类似,但从最后一个元素开始,然后继续向前。注意,在从右往左折叠或归约时,操作的参数顺序会改变:先是元素,然后是累积值。
1
2
3
4
5
6
|
fun main() {
val numbers = listOf(5, 2, 10, 4)
val sumDoubledRight = numbers.foldRight(0) { element, sum -> sum + element * 2 }
println(sumDoubledRight)
}
|
你也可以应用接收元素索引作为参数的操作。为此,请使用 reduceIndexed() 和 foldIndexed() 函数,把元素索引作为操作的第一个参数传入。
最后,还有一些函数会从右到左对集合元素应用这类操作——reduceRightIndexed() 和 foldRightIndexed()。
1
2
3
4
5
6
7
8
9
|
fun main() {
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)
}
|
所有 reduce 操作在空集合上都会抛出异常。要改为得到 null,请使用它们的 *OrNull() 版本:
如果你想保存中间累积值,可以使用 runningFold()(或其同义词 scan())和 runningReduce() 函数。
1
2
3
4
5
6
7
8
9
|
fun main() {
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 }
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() 或 reduceIndexed 的对应版本。