侧边栏壁纸
博主头像
Z同学博主等级

工作磨平激情前,坚持技术的热忱。 欢迎光临Z同学的技术小站。 分享最新的互联网知识。

  • 累计撰写 274 篇文章
  • 累计创建 55 个标签
  • 累计收到 74 条评论

Kotlin 集合 聚合操作详解

Z同学
2021-10-13 / 0 评论 / 0 点赞 / 517 阅读 / 4,294 字
温馨提示:
本文最后更新于 2021-11-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

什么是聚合操作?聚合操作是指基于集合内容返回单个值的操作。

例如返回集合中的最大值,或者最小值。

返回集合中的平均值。

返回集合参数累计和。

返回集合元素总数量。

等等。

这些操作,我们称之为聚合操作。如果对SQL语法比较熟悉小伙伴。那就应该能够更清晰的理解聚合的含义了。

引读

于集合有关系的,其他几篇文章介绍。

Kotlin 集合 基本介绍 - Z同学 (zinyan.com)

Kotlin 集合 转换,过滤和检测 - Z同学 (zinyan.com)

Kotlin 集合 plus,minus和分组group详解 - Z同学 (zinyan.com)

Kotlin 集合 查询,检测,截取等方法介绍 - Z同学 (zinyan.com)

Kotlin 集合 排序详解 - Z同学 (zinyan.com)

常见聚合函数

主要介绍一些比较常见的聚合函数。其他开发语言中也都有大同小异的方法。

示例:

fun main(string: Array<String>) {
    val text = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
    println("列表数量:${text.count()}")
    println("返回集合最大值Max:${text.maxOrNull()}")
    println("返回集合最小值Min:${text.minOrNull()}")
    println("返回平均值Average:${text.average()}")
    println("返回总和Sum:${text.sum()}")
}
//返回
列表数量:9
返回集合最大值Max:9
返回集合最小值Min:1
返回平均值Average:5.0
返回总和Sum:45

我们如果判断的是字符串,我们怎么判断字符串最大和最小呢?

那么我们就可以使用自定义方式,实现判断逻辑

示例:

fun main(string: Array<String>) {
    val text = listOf("one", "two", "three")
    println("列表数量:${text.count()}")
    println("返回集合最大值Max:${text.maxOrNull()}")
    println("返回集合最小值Min:${text.minOrNull()}")
    println(
        "返回集合字符长度最大的:${
            text.maxByOrNull {
                //自定义 按照字符长度判断,最大
                it.length
            }
        }"
    )
}
//输出
列表数量:3
返回集合最大值Max:two
返回集合最小值Min:one
返回集合最小值Min:three

其他的几种聚合方法,都是可以扩展的。我们都可以通过传入表达式,扩展我们的计算需求。

Fold() 和 Reduce()

特定状态下,可以使用fold和reduce 。进行聚合操作。

这两个方法主要就是可以将集合对象按照自定义的方式进行累积。

fold: 你可以定义初始累积值。

reduce:不能定义初始累积值,从集合第一个元素开始累积。

结合示例我们来理解一下这两个函数的意义吧。

要求:将集合中的偶数值进行累加求和

示例:

fun main(string: Array<String>) {
    val text = listOf(1, 2, 3, 4, 5, 6)
    //sum 是求和后的结果,element 是当前的判断元素
    val ss = text.fold(0) { sum, element ->
        if (element % 2 == 0) {
            sum + element
        } else {
            sum
        }
    }
    println(ss)
}
//输出
12

上面的例子,为什么不用reduce 。那是因为如果是reduce的话。第一个元素初始化时,就是sum值了。

直接使用reduce的话,返回值就是13=1+2+4+6

下面简单理解一下两者的区别:

示例:

如果是做累加计算:

fun main(string: Array<String>) {
    val text = listOf(1, 2, 3)
    //sum 是求和后的结果,element 是当前的判断元素
    val ss1 = text.fold(0) { sum, element -> sum + element }
    val ss2 = text.reduce { sum, element -> sum + element }
    println(ss1)
    println(ss2)
}
//输出
6
6

感觉都一样是吧。

但是如果我们针对element的参数,进行一个计算然后再累加就会出现数值偏移了

示例:

fun main(string: Array<String>) {
    val text = listOf(1, 2, 3)
    //sum 是求和后的结果,element 是当前的判断元素
    val ss1 = text.fold(0) { sum, element -> sum + element*2 }
    val ss2 = text.reduce { sum, element -> sum + element*2 }
    println(ss1)
    println(ss2)
}
//输出
12
11

那是因为在reduce中,第一次循环时,sum = 1,element=2 。 然后再执行了计算。所以少了

这就是fold和reduce的差别所在。

总结:在fold和reduce中,第一个参数是累积值,第二个参数是集合元素变量

sum 除了是累加的结果值,也可以是累积,可以累除,可以字符串拼接等等。

foldOrNull()和reduceOrNull()

我们如果直接使用fold 进行聚合操作时,集合是空,那么会直接报出异常。

所以针对该情况, kotlin提供了*OrNull方法。在集合元素null的情况下。避免异常,直接返回null

示例:

fun main(string: Array<String>) {
    val strings = listOf("A", "B", "C", "D")
    println(strings.reduceOrNull { sum, element -> sum + element })
    println(strings.reduceIndexedOrNull { index, sum, element -> sum + element + index })
    println(emptyList<String>().reduceOrNull { sum, element -> sum + element })
}
//输出
ABCD
AB1C2D3
null

其他的几种方法的OrNull都是大同小异。就不做详细介绍了。

foldRight和reduceRight

功能和fold与reduce是一样的。只是顺序进行了跳转。它会按照集合的倒叙也就是从右往左进行元素遍历计算。

在foldRight和reduceRight中,第一个参数变成了集合元素变量,第二个参数变成了累计值。

示例:

fun main(string: Array<String>) {
    val text = listOf(1, 2, 3)
    //sum 是累积计算的结果,element 是当前的判断元素
    val ss1 = text.foldRight(0) { element, sum -> sum + element * 2 }
    val ss2 = text.reduceRight() { element, sum -> sum + element * 2 }
    println(ss1)
    println(ss2)
}
//输出
12
9

我们会发现,reduceRight 和reduce 返回的结果一个是11一个是9.

为什么?

因为倒叙计算一开始
第一轮:element =2,sum=3 : 3+2*2= 7

第二轮:element =1,sum =7 : 7+1*2 =9

就是这个结果了。

foldIndexed() 和reduceIndexed()

我们如果在集合聚合操作的时候,也需要下标参与。那么就可以使用这两个函数了。

第一个元素就是下标,第二个元素是累积值, 第三个元素就是当前集合变量。

示例: 将集合下标是偶数的值进行累加计算

fun main(string: Array<String>) {
    val text = listOf(1, 2, 3)
    //sum 是累积计算的结果,element 是当前的判断元素,index 是元素下标
    val sss = text.foldIndexed(0) { index, sum, element ->
        if (index % 2 == 0)
            sum + element
        else sum
    }
    println(sss)
    val sss1 = text.reduceIndexed { index, sum, element ->
        if (index % 2 == 0)
            sum + element
        else sum
    }
    println(sss1)
}
//输出
4
4

这只是一个简单的应用场景,具体可以根据我们的需求,做更复杂的展开和计算。

但是,必须比较清晰的弄明白fold和reduce的计算规则。否则你会发现数据某种情况可以,某种情况又不可以的情况发生。

foldRightIndexed()和reduceRightIndexed()

从右往左计算,并支持index取值的场景。

功能和foldRightIndexed等一样的,只是顺序参数有变化而已。

示例:

fun main(string: Array<String>) {
    val text = listOf(1, 2, 3)
    //sum 是累积计算的结果,element 是当前的判断元素,index 是元素下标
    val sss = text.reduceRightIndexed { index, element, sum ->
        println("sum: $sum,elemen:$element, index:$index")
        sum + element+index
     }
    println(sss)
}
//输出
sum: 3,elemen:2, index:1
sum: 6,elemen:1, index:0
7

我将计算中的几个元素打印一下,大家就能更清晰的理解了。

0

评论区