Kotlin的集合是让我为之心动的地方,丰富的高阶函数帮助我们高效开发。今天介绍Kotlin的基础集合用法、获取集合元素的函数、过滤元素的函数、元素排序的函数、元素统计的函数、集合元素映射的函数、集合的交差并补集的函数。还有一些工作中的经验。

初始化集合

和Java集合不同的是,Kotlin的集合分可变和不可变两种集合。同时也支持两种集合相互切换。

List集合
// 声明并初始化不可变List集合 val list: List<Any> = listOf<Any>(1, "2", 3) //
声明并初始化可变MutableList集合 val mutableList: MutableList<Any> = mutableListOf<Any>(4,
"5", 6) mutableList.add("7") list.map { print("$it \t") } mutableList.map {
print("$it \t") }
Set集合
// 声明并初始化不可变Set集合 val set: Set<Any> = setOf<Any>(1, "2", 3, "3") //
声明并初始化可变MutableSet集合 val mutableSet: MutableSet<Any> = mutableSetOf<Any>(4,
"5", 6) mutableSet.add(6) set.map { print("$it \t") } mutableSet.map {
print("$it \t") }
Map集合
// 声明并初始化不可变Map集合 val map: Map<String, Any> = mapOf("k1" to "v1" , "k2" to 3)
// 声明并初始化可变MutableMap集合 val mutableMap: MutableMap<String, Any> =
mutableMapOf("k1" to "v1" , "k1" to 3) map.map { println("key : ${it.key} \t
value : ${it.value}") } mutableMap.map { println("key : ${it.key} \t value :
${it.value}") }
集合高阶函数

获取集合元素


用Java语言开发时,我们通常用循环遍历集合的每个元素。有时候也会通过下标直接获取指定元素。此时原则上时需要我们先考虑集合元素的长度,以避免下标越界的异常问题。但往往我们会抱着侥幸的心态直接通过
get(index)
方法获取元素。一般情况下我们会在黑盒自测中发现越界问题(有部分朋友从不黑盒,直接白盒测试,并反问:测试的工作难道不就是发现问题?)。即便是在运行中出现越界问题,也可以甩锅给数据库。但不管怎么样,因为越界导致系统不稳定是不合理的。

用Kotlin语言开发时,我们会发现有很多带有"Or"字样的方法。比如我常用的getOrElse,firstOrNull
等方法。分别表示:通过下标如果没有获取到值,则返回自定的值。和获取集合的第一个元素,若集合为空则返回null。正因为Kotlin提供了很多类似getOrElse
,firstOrNull
的方法。很大程度上提高了我们的开发效率,和减少了一些低级错误发生的概率。接下来我们学习一下Kotlin具体有哪些获取集合元素的方法(single方法没怎么用过)

常用函数

* get(index) : List的函数,通过下标获取指定元素。若找不到值(下标越界),会抛出IndexOutOfBoundsException异常
* getOrElse(index, {...}) : List的扩展函数,通过下标获取指定元素。找不到值则返回默认值
* getOrNull(index) : List的扩展函数,通过下标获取指定元素。找不到值则返回null
* elementAtOrElse(index, {...}) : Iterable接口的扩展函数,功能同getOrElse 方法
* elementAtOrNull(index) : Iterable接口的扩展函数,功能同getOrNull 方法
* 注意get方法是List独有,其他集合可以用element方法。
* first() : 获取集合第一个元素。若没有返回值,则抛出NoSuchElementException异常
* first{} : 获取集合中指定元素的第一个元素。若没有返回值,则抛出NoSuchElementException异常
* firstOrNull() : 获取集合第一个元素。若没有返回值,返回null
* firstOrNull{} : 获取集合指定元素的第一个元素。若没有返回值,返回null
* 看到这里,是不是有点明白Kotlin获取元素的规则:如果没有则怎么样
* last() : 与first()相反
* last{} : 与first{}相反
* lastOrNull{} : 与firstOrNull()相反
* lastOrNull() : 与firstOrNull{}相反
* indexOfFirst{...} : 返回集合中第一个满足条件元素的下标
* indexOfLast{...} : 返回集合中最后一个满足条件元素的下标
* 咋也不知道single方法设计的初衷,咋也不敢问
* single() : Returns the single element, or throws an exception if the
collection is empty or has more than one element.官方api文档地址
<https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/single.html>
* single{} : 按照条件返回单个元素,若集合为空或者有多个元素满足条件,则报错
* singleOrNull() : 返回单个元素,若集合为空或者有多个元素,则返回null
* singleOrNull{} : 按照条件返回单个元素,若集合为空或者有多个元素满足条件,则返回null
使用建议

在使用获取元素的方法时,推荐方法名中带有"Or"字样的方法,可以减少很多不必要的报错。

List集合通过下标获取元素可以用get,getOrElse,getOrNull函数,但其他集合没有这些方法。

笔者单方面认为single函数和数据库的唯一约束的功能有点类似,在使用Kotlin的过程中,你会发现它有很多和数据库类似的功能。

基础用法
val list: MutableList<Int> = mutableListOf(1,2,3,4,5) println("getOrElse :
${list.getOrElse(10,{ 20 })}") println("getOrNull : ${list.getOrNull(10)}")
println("firstOrNull : ${list.firstOrNull()}") println("firstOrNull :
${list.firstOrNull { it > 3 }}") println("indexOfFirst : ${list.indexOfFirst {
it > 3 }}") println("indexOfLast : ${list.indexOfLast { it > 3 }}")
----------------------------------------------------- getOrElse : 20 getOrNull
: null firstOrNull : 1 firstOrNull : 4 indexOfFirst : 3 indexOfLast : 4
集合元素排序


用Java语言开发时,给对象集合做排序是常有的业务逻辑。(Java8之后的写法不太了解)按照我之前工作中排序的代码其实也并不复杂,十行代码基本可以搞定一个排序逻辑。注意是一个,一个。业务中存在大量的排序需求,这种代码会反复出现。对于我这种佛系程序员兼CV高手而言,早已经习以为常了。但自从用了Kotlin的
sortedBy方法后。突然觉得Kotlin用起来倍儿爽!

用Java7开发了几年,Java8只接触了一点皮毛,现在Java12都已经出来了。经常看到一些文章为了突出某个语言的强大,而去踩其他语言。我只想问:who
are you?每个语言都有自己独特的一面.神仙打架,我们负责吃瓜就好。就懂点皮毛的人,瞎掺和啥?
Collections.sort(list,new Comparator () { @Override public int compare(Object
o1, Object o2) { return o1.compareTo(e2); } });
用Kotlin语言开发时,我们不需要重复写类似上面的排序代码,Kotlin已经帮我们封装好了,只需要我们写需要排序的字段即可。其底层也是通过Java
的Collections.sort实现的。所有我们就放心大胆的用吧。
public inline fun <T, R : Comparable<R>> MutableList<T>.sortBy(crossinline
selector: (T) -> R?): Unit { if (size > 1) sortWith(compareBy(selector)) }
@kotlin.jvm.JvmVersion public fun <T> MutableList<T>.sortWith(comparator:
Comparator<in T>): Unit { if (size > 1) java.util.Collections.sort(this,
comparator) }
常用函数

* sortedBy{} : 根据条件给集合升序,常用与给对象集合的某个字段排序,并返回排序后的集合,原集合顺序不变
* reversed() : 集合反序。与降序不同,反序指的是和初始化的顺序相反
* sorted() : 自然升序,常用于给普通集合排序
* sortedDescending() : 自然降序
* sortedByDescending{} : 根据条件给集合降序
* ed结尾的排序方法,是不会对原集合进行修改,而是返回一个排序后的新集合。没有以ed结尾的方法恰恰相反 ---来自一个不严谨的总结
* sortBy{} : 根据条件给原集合升序,常用与给对象集合的某个字段排序
* sortByDescending{} : 根据条件给原集合降序
* reverse() : 原集合反序
使用建议

千万不要把反序理解成了倒序,前车之鉴

sortBy方法是对原集合做排序操作,而sortedBy方法是返回一个排序后的新集合,原集合排序没有变

kotlin排序方法中可以用and,or 组装多个条件,但效果并不理想

基础用法
data class Person( var name: String = "", var age: Int = 0, var salary: Double
= 0.0 ) val persons = mutableListOf(Person("n1", 20, 2000.0), Person("n2", 24,
4000.0), Person("n3", 28, 6000.0), Person("n4", 26, 8000.0), Person("n5", 34,
7000.0), Person("n6", 44, 5000.0)) persons.sortedBy { it.age }.map {
println(it) } persons.map { it.age }.sorted() persons.sortBy { it.age }
persons.reversed()
过滤元素


Java8也提供了Map和Filter函数用于转换和过滤对象,使开发变得更轻松,遥想当年在for循环里面加if语句。慢慢成了过去式。集合遍历之前先filter一下,已经成了我开发过程中不可或缺的一步。虽然
filter 函数相对于Kotlin的 getOrNull 和 sortedBy 函数,并没有给人一种眼前一亮的感觉。但它提高了代码的可读性和美观性。

常用函数

* filter{...} : 过滤不满足条件的元素,返回只满足条件元素列表,不影响原集合
* filterNot{...} : 和filter{}函数的功能相反
* filterNotNull() : 过滤掉集合中为null的元素
* filterIndexed{...} : 在filter{}函数上多了一个下标功能,可以通过索引进一步过滤
* Kotlin的函数是见名知意,非常好用,上手也快,弄明白一个方法,其他方法都没大的问题
* distinct() : 去除重复元素,返回元素的顺序和原集合顺序一致
* distinctBy{...} : 根据操作元素后的结果去去重,去除的是操作前的元素
* take(num) : 返回集合中前num个元素组成的集合
* takeWhile{...} : 从第一个元素开始遍历集合,当出现第一个不满足条件元素时退出循环。返回所有满足条件的元素集合
* takeLast(num) : 和take 函数相反,返回集合中后num个元素组成的集合
* takeLastWhile{...} : 从最后一个元素开始遍历集合,当出现第一个不满足条件元素时退出循环。返回所有满足条件的元素集合
* 不要被这么多方法吓到,学了take函数的用法,takeLast、drop、dropLast的用法都可以猜到
* drop(num) : 过滤集合中前num个元素
* dropWhile{...} : 和执行takeWhile{...}函数后得到的结果相反
* dropLast(num) : 过滤集合中后num个元素
* dropLastWhile{...} : 和执行takeLastWhile{...}函数后得到的结果相反
* slice(...) : 过滤掉所有不满足执行下标的元素。参数是下标集合或者是下标区间。
使用建议

以上Filter、Distinct、Take、Drop、Slice方法都返回一个处理后的新集合,不影响原集合。

Kotlin提供了丰富的函数供我们使用,同时也吓退了很多朋友,别怕!Kotlin的函数都是买一送一的,学会一个,不愁另一个。

基础用法
val list = listOf(-3,-2,1,3,5,3,7,2,10,9) println("filter : ${list.filter { it
> 1 }}") println("filterIndexed : ${list.filterIndexed { index, result -> index
% 2 == 0 && result > 5 }}") println("take : ${list.take(5)}")
println("takeWhile : ${list.takeWhile { it < 5 }}") println("drop :
${list.drop(5)}") println("distinct : ${list.distinct()}") println("distinctBy
: ${list.distinctBy { it % 2 }}") println("slice :
${list.slice(IntRange(1,5))}")
----------------------------------------------------- filter : [3, 5, 3, 7, 2,
10, 9] filterIndexed : [7, 10] take : [-3, -2, 1, 3, 5] takeWhile : [-3, -2, 1,
3] drop : [3, 7, 2, 10, 9] distinct : [-3, -2, 1, 3, 5, 7, 2, 10, 9] distinctBy
: [-3, -2, 1] slice : [-2, 1, 3, 5, 3]
统计元素

在用Java8和Kotlin之前。和排序一样,在实现求最大值、平均值、求和等操作时,都要写很多冗余的代码。现在好了,Kotlin已经封装了这些方法。朋友们,
千万不要过于依赖这些方法
。有些一条sql能解决的问题,就不要把统计的逻辑留给代码完成。这里的方法更适合在业务处理过程中,对一些简单集合的统计处理。如果是统计报表的功能,就不要有什么歪心思了。分享一篇关于统计的文章:
常见的统计解决方案 <https://www.cnblogs.com/itdragon/p/9372229.html>

常用函数

* max() : 获取集合中最大的元素,若为空元素集合,则返回null
* maxBy{...} : 获取方法处理后返回结果最大值对应那个元素的初始值,如果没有则返回null
* min() : 获取集合中最小的元素,若为空元素集合,则返回null
* minBy{...} : 获取方法处理后返回结果最小值对应那个元素的初始值,如果没有则返回null
* sum() : 对集合原元素数据进行累加,返回值类型是Int
* sumBy{...} : 根据元素运算操作后的结果进行累加,返回值类型是Int
* sumByDouble{...} : 和sumBy{}相似,但返回值类型是Double
* average() : 对集合求平均数
* reduce{...} : 从集合中的第一个元素到最后一个元素的累计操作
* reduceIndexed{...} : 在reduce{}函数基础上多了一个下标功能
* reduceRight{...} : 与reduce{...} 相反,该方法是从最后一个元素开始
* reduceRightIndexed{...} : 在reduceRight{}函数基础上多了一个下标功能
* fold{...} : 和reduce{}类似,但是fold{}有一个初始值
* foldIndexed{...} : 和reduceIndexed{}类似,但是foldIndexed{}有一个初始值
* foldRight{...} : 和reduceRight{}类似,但是foldRight{}有一个初始值
* foldRightIndexed{...} : 和reduceRightIndexed{}类似,但是foldRightIndexed{}有一个初始值
* any{...} : 判断集合中是否存在满足条件的元素
* all{...} : 判断集合中的所有元素是否都满足条件
* none{...} : 和all{...}函数的作用相反
使用建议

不能过于依赖Kotlin的统计方法,这些方法更适合一些业务逻辑上的简单统计处理,不适合数据统计功能。

注意sum函数返回结果是Int类型,如果是Double则需要用sumByDouble方法。

基础用法
val persons = mutableListOf(Person("n1", 20, 2000.0), Person("n2", 24,
4000.0), Person("n3", 28, 6000.0), Person("n4", 26, 8000.0), Person("n5", 34,
7000.0), Person("n6", 44, 5000.0)) println("maxBy : ${persons.maxBy { it.age
}}") println("sumByDouble : ${persons.sumByDouble { it.salary }}")
println("average : ${persons.map { it.salary }.average()}") println("any :
${persons.any { it.salary < 1000 }}")
----------------------------------------------------- maxBy : Person(name=n6,
age=44, salary=5000.0) sumByDouble : 32000.0 average : 5333.333333333333 any :
false
元素映射


Kotlin提供了一个遍历集合的forEach方法,也提供了对集合每个元素都进行指定操作并返回一个新集合的map方法。map方法是可以遍历集合,但如果误将其认为遍历集合的方法,同样会将mapNotNull方法误以为成遍历非null元素的方法。

常用方法

* map{...} : 把每个元素按照特定的方法进行转换,并返回一个新的集合
* mapNotNull{...} : 同map{}相同,过滤掉转换之后为null的元素
* mapIndexed{index,result} : 在map{} 函数上多了一个下标功能
* mapIndexedNotNull{index,result} : 在mapNotNull{}函数上多了一个下标功能
* flatMap{...} : 根据条件合并两个集合,组成一个新的集合
* groupBy{...} : 分组。即根据条件把集合拆分为为一个Map<K,List<T>>类型的集合
使用建议

map方法不是集合遍历,集合遍历的方法是forEach。

mapNotNull方法不是遍历集合不为null的方法,而是过滤转换后为null的元素。

调用string.split()函数,无论用forEach还是map,即使没有内容还是会遍历一次。

基础用法
val list = listOf(-3,-2,1,3,5,3,7,2,10,9) list.map { it + 1 }.forEach {
print("$it \t") } list.mapIndexedNotNull { index, value -> if (index % 2 == 0)
value else null }.forEach { print("$it \t") } println("flatMap : ${list.flatMap
{ listOf(it, it + 1,"n$it") }}") println("groupBy : ${list.groupBy { if (it % 2
== 0) "偶数" else "奇数" }}")
集合的交差并补操作


对集合的求交差集是一个常用的方法。比如前端需要将更新,创建,删除的逻辑用一个接口完成。我们可以通过旧数据与新数据求差集找出需要删除的数据。通过新数据和旧数据求差集找出需要创建的数据。通过求交集找出需要更新的数据。

* intersect(...) : 返回一个集合,其中包含此集合和指定集合所包含的所有元素,交集
* subtract(...) : 返回一个集合,其中包含此数组包含但未包含在指定集合中的所有元素,差集
* union(...) : 返回包含两个集合中所有不同元素的集合,并集
* minus(...) : 返回包含原始集合的所有元素的列表,但给定的数组中包含的元素除外,补集
基础用法
val list1 = mutableListOf(1,2,3,4,5) val list2 = mutableListOf(4,5,6,7)
println("intersect : ${list1.intersect(list2)}") println("subtract :
${list1.subtract(list2)}") println("union : ${list1.union(list2)}")
println("minus : ${list1.minus(list2)}")
----------------------------------------------------- intersect : [4, 5]
subtract : [1, 2, 3] union : [1, 2, 3, 4, 5, 6, 7] minus : [1, 2, 3]
官网地址:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/
<https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/>


到这里文章就结束了。如果用好集合的高阶函数,可以让我们的开发效率有明显的提高,bug的数量也会锐减。文章还有一部分内容没有介绍。我在工作用中集合就用MutableList、MutableSet、MutableMap,可Java中还有ArrayList,LinkedList,HashMap,HashSet等集合Kotlin中也有这些。一直都没有好好研究,这个坑先挖好,后来再补上。