Skip to content

彙總操作

Kotlin 集合包含用於常用 彙總操作 的函式——這些操作基於集合內容返回單一值。其中大多數都廣為人知,並且與其他語言中的運作方式相同:

  • minOrNull()maxOrNull() 分別返回最小和最大的元素。在空集合上,它們返回 null
  • average() 返回數字集合中元素的平均值。
  • sum() 返回數字集合中元素的總和。
  • count() 返回集合中元素的數量。
kotlin

fun main() {
    val numbers = listOf(6, 42, 10, 4)

    println("計數: ${numbers.count()}")
    println("最大值: ${numbers.maxOrNull()}")
    println("最小值: ${numbers.minOrNull()}")
    println("平均值: ${numbers.average()}")
    println("總和: ${numbers.sum()}")
}

還有一些函式可以透過特定的選擇器函式或自訂的 Comparator 來取得最小和最大的元素:

這些函式在空集合上返回 null。還有一些替代方案——maxOfminOfmaxOfWithminOfWith——它們與對應的函式功能相同,但在空集合上會拋出 NoSuchElementException

kotlin

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(),它接受一個選擇器函式,並返回其應用於所有集合元素的總和。選擇器可以返回不同的數字類型:IntLongDoubleUIntULong (在 JVM 上也支援 BigIntegerBigDecimal)。

kotlin

fun main() {
    val numbers = listOf(5, 42, 10, 4)
    println(numbers.sumOf { it * 2 })
    println(numbers.sumOf { it.toDouble() / 2 })
}

摺疊與歸約

對於更特定的情況,有 reduce()fold() 函式,它們依序將提供的操作應用於集合元素並返回累計結果。該操作接受兩個引數:先前累計的值和集合元素。

這兩個函式之間的區別在於,fold() 接受一個初始值並在第一步中將其用作累計值,而 reduce() 的第一步則使用第一個和第二個元素作為操作引數。

kotlin
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(),但從最後一個元素開始,然後繼續到前一個元素。請注意,當向右摺疊或歸約時,操作引數會改變它們的順序:首先是元素,然後是累計值。

kotlin

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()

kotlin

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)
}

所有歸約操作在空集合上都會拋出例外。若要改為接收 null,請使用其 *OrNull() 對應函式:

對於您想要儲存中間累計值的情況,有函式 runningFold()(或其同義詞 scan())和 runningReduce()

kotlin

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("
", "使用 runningReduce 的前 N 個元素的總和:
"))
    println(runningFoldSum.mapIndexed(transform).joinToString("
", "使用 runningFold 的前 N 個元素的總和:
"))
}

如果您需要在操作參數中使用索引,請使用 runningFoldIndexed()runningReduceIndexed()