前言
什么是聚合操作?聚合操作是指基于集合内容返回单个值的操作。
例如返回集合中的最大值,或者最小值。
返回集合中的平均值。
返回集合参数累计和。
返回集合元素总数量。
等等。
这些操作,我们称之为聚合操作。如果对SQL语法比较熟悉小伙伴。那就应该能够更清晰的理解聚合的含义了。
引读
集合有关系的,其他几篇文章介绍。
Kotlin 集合 基本介绍 - Z同学 (zinyan.com)
Kotlin 集合 转换,过滤和检测 - Z同学 (zinyan.com)
Kotlin 集合 plus,minus和分组group详解 - Z同学 (zinyan.com)
Kotlin 集合 查询,检测,截取等方法介绍 - Z同学 (zinyan.com)
Kotlin 集合 排序详解 - Z同学 (zinyan.com)
常见聚合函数
主要介绍一些比较常见的聚合函数。其他开发语言中也都有大同小异的方法。
示例:
代码语言:javascript复制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
我们如果判断的是字符串,我们怎么判断字符串最大和最小呢?
那么我们就可以使用自定义方式,实现判断逻辑
示例:
代码语言:javascript复制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:不能定义初始累积值,从集合第一个元素开始累积。
结合示例我们来理解一下这两个函数的意义吧。
要求:将集合中的偶数值进行累加求和
示例:
代码语言:javascript复制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
下面简单理解一下两者的区别:
示例:
如果是做累加计算:
代码语言:javascript复制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的参数,进行一个计算然后再累加就会出现数值偏移了
示例:
代码语言:javascript复制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
示例:
代码语言:javascript复制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中,第一个参数变成了集合元素变量,第二个参数变成了累计值。
示例:
代码语言:javascript复制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()
我们如果在集合聚合操作的时候,也需要下标参与。那么就可以使用这两个函数了。
第一个元素就是下标,第二个元素是累积值, 第三个元素就是当前集合变量。
示例:将集合下标是偶数的值进行累加计算
代码语言:javascript复制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等一样的,只是顺序参数有变化而已。
示例:
代码语言:javascript复制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
我将计算中的几个元素打印一下,大家就能更清晰的理解了。