34

初识Kotlin之集合

 4 years ago
source link: https://www.tuicool.com/articles/uuMbEne
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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"字样的方法。比如我常用的 getOrElsefirstOrNull 等方法。分别表示:通过下标如果没有获取到值,则返回自定的值。和获取集合的第一个元素,若集合为空则返回null。正因为Kotlin提供了很多类似 getOrElsefirstOrNull 的方法。很大程度上提高了我们的开发效率,和减少了一些低级错误发生的概率。接下来我们学习一下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的 getOrNullsortedBy 函数,并没有给人一种眼前一亮的感觉。但它提高了代码的可读性和美观性。

常用函数

  • 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]

官网地址: https://kotlinlang.org/api/la...

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK