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 文档地址
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 能解决的问题, 就不要把统计的逻辑留给代码完成. 这里的方法更适合在业务处理过程中, 对一些简单集合的统计处理. 如果是统计报表的功能, 就不要有什么歪心思了. 分享一篇关于统计的文章: 常见的统计解决方案
常用函数
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]
官网地址:
到这里文章就结束了. 如果用好集合的高阶函数, 可以让我们的开发效率有明显的提高, bug 的数量也会锐减. 文章还有一部分内容没有介绍. 我在工作用中集合就用 MutableList,MutableSet,MutableMap, 可 Java 中还有 ArrayList,LinkedList,HashMap,HashSet 等集合 Kotlin 中也有这些. 一直都没有好好研究, 这个坑先挖好, 后来再补上.
来源: https://www.cnblogs.com/itdragon/p/10887579.html