Kotlin入门第二课:集合操作
测试项目Github地址:
前文传送:
1. 介绍
作为Kotlin入门的第二课,不打算按照教程从基础数据类型开始,而是直接学习至关重要的集合部分。因为一般的应用开发都离不开数据,数据处理就要用到集合,而只有深入了解集合,包括概念及不同类型的集合分别实现了哪些方法,才能在需要的时候快速选出最合适的集合与对应的操作。因此,迫不及待地想给大家展示Kotlin集合的魅力,基础数据类型的用法会放到后续的文章进行整理。
Kotlin中的集合主要有以下几种:
Iterable--An iterator over a collection or another entity that can be represented as a sequence of elements;
MutableIterable--An iterator over a mutable collection. Provides the ability to remove elements while iterating;
Collection--A generic collection of elements. Methods in this interface support only read-only access to the collection;
MutableCollection--A generic collection of elements that supports adding and removing elements;
List--A generic ordered collection of elements. Methods in this interface support only read-only access to the list;
MutableList--A generic ordered collection of elements that supports adding and removing elements;
Set--A generic unordered collection of elements that does not support duplicate elements;
MutableSet--A generic unordered collection of elements that does not support duplicate elements, and supports adding and removing elements;
Map--A collection that holds pairs of objects (keys and values) and supports efficiently retrieving the value corresponding to each key. Map keys are unique; the map holds only one value for each key;
MutableMap--A modifiable collection that holds pairs of objects (keys and values) and supports efficiently retrieving the value corresponding to each key. Map keys are unique; the map holds only one value for each key;
不专业的翻译会误导读者,所以这里就不献丑了,相信这段英文解释对程序员来说不成问题。
2. 操作方法
涉及到的代码在KotlinForJava的Kotlin1项目中,针对集合List和MutableList的操作进行测试,参考的是Kotlin中文学习资料,前面给出的文章中能找到相应的资源链接。
学习的同时通过编码练习是很有必要的,除了加深理解还可以发现资料中存在的问题,常见的如IDEA或API更新了而资料是旧的,花时间去学习已经废弃的方法就不值得了。所以,建议英文好的通过官网给出的资料来学习是最好的,上面的信息一般会及时更新。
先定义两个List对象,后面的操作会用到。
1 val list = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 2 val mutableList = mutableListOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
2.1 总数操作
测试代码:
1 println(list.any { it % 2 == 1 }) 2 3 println(list.all { it % 2 == 1 }) 4 5 println(list.count { it % 2 == 1 }) 6 7 println(list.fold(10) { total, next -> total + next }) 8 println(list.foldRight(10) { total, next -> total + next }) 9 10 list.forEach { value -> if (value > 8) println(value) } 11 list.forEachIndexed { index, value -> if (value > 8) println("value of index $index is $value") } 12 13 println(list.max()) 14 println(list.maxBy { -it }) 15 16 println(list.min()) 17 println(list.minBy { -it }) 18 19 println(list.none { it % 2 == 10 }) 20 21 println(list.reduce { total, next -> total + next }) 22 println(list.reduceRight { total, next -> total + next }) 23 24 println(list.sumBy { it % 2 })
方法作用:
any--判断集合中是否有满足条件 的元素;
all--判断集合中的元素是否都满足条件;
count--查询集合中满足条件的元素个数;
fold--在给定初始值的基础上,从第一项到最后一项进行累加;
foldRight--在给定初始值的基础上,从最后一下到第一项进行累加,与fold只是的方向不同;
forEach--循环遍历元素,元素是it,可对每个元素进行相关操作;
forEachIndexed--循环遍历元素,同时得到元素index(下标);
max--查询最大的元素,如果没有则返回null;
maxBy--获取方法处理后返回结果最大值对应的那个元素的初始值,如果没有则返回null;
min--查询最小的元素,如果没有则返回null;
minBy--获取方法处理后返回结果最小值对应那个元素的初始值,如果没有则返回null;
none--判断集合中是否都不满足条件,是则返回true;
reduce--与fold区别在于没有初始值,或者说初始值为0,从第一项到最后一项进行累加;
reduceRight--从最后一项到第一项进行累加,与reduce只是方向的不同;
sumBy--获取方法处理后返回结果值的总和;
建议将文字解释和代码结合起来理解方法的作用,先对结果有一个预判,然后看下面的打印信息。
打印结果:
true false 5 55 55 9 value of index 9 is 9 9 0 0 9 true 45 45 5
2.2 过滤操作
测试代码:
1 println(list.drop(4)) 2 println(list.dropWhile { it < 9 }) 3 println(list.dropLastWhile { it < 9 }) 4 5 println(list.filter { it % 2 == 0 }) 6 println(list.filterNot { it % 2 == 0 }) 7 println(list.filterNotNull()) 8 9 println(list.slice(listOf(0, 4, 8))) 10 //println(list.slice(listOf(0, 4, 80))) //java.lang.ArrayIndexOutOfBoundsException: 80 11 12 println(list.take(2)) 13 println(list.takeLast(2)) 14 println(list.takeWhile { it < 3 })
方法作用:
drop--返回去掉前n个元素后的列表;
dropWhile--返回从第一项起,去掉满足条件的元素,直到不满足条件的一项为止;
dropLastWhile--返回从最后一项起,去掉满足条件的元素,直到不满足条件的一项为止;
filter--过滤掉所有不满足条件的元素;
filterNot--过滤掉所有满足条件的元素;
filterNotNull--过滤掉所有值为null的元素;
slice--过滤掉非指定下标的元素,即保留下标对应的元素过滤List中指定下标的元素(比如这里只保留下标为1,3,4的元素),当过滤list中有元素值大于目标List大小时会出现异常;
take--返回从第一个开始的n个元素;
takeLast--返回从最后一个开始的n个元素;
takeWhile--返回不满足条件的下标前面的所有元素的集合;
代码中有一行注释,关于slice操作,在实际使用时需要注意过滤List中的元素值,以免出现ArrayIndexOutOfBoundsException异常。
打印结果:
[4, 5, 6, 7, 8, 9] [9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 2, 4, 6, 8] [1, 3, 5, 7, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 4, 8] [0, 1] [8, 9] [0, 1, 2]
2.3 映射操作
测试代码:
1 println(list.flatMap { listOf(it, it + 1) }) 2 3 println(list.groupBy { if (it % 2 == 0) "even" else "odd" }) 4 5 println(list.map { it * 2 }) 6 println(list.mapIndexed { index, it -> index * it }) 7 println(list.mapNotNull { it * 2 })
方法作用:
flatMap--合并两个集合,可以在合并的时候对迭代元素值it多想要的操作;
groupBy--将集合中的元素按照某个条件分组,返回Map;
map--将集合中的元素通过某个方法转换后的结果存到一个集合中;
mapIndexed--除了得到转换后的结果,还可以拿到index(下标);
mapNotNull--执行方法转换前过滤掉为null的元素;
打印结果:
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10] {even=[0, 2, 4, 6, 8], odd=[1, 3, 5, 7, 9]} [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
2.4 元素操作
测试代码:
1 println(list.contains(2)) 2 3 println(list.elementAt(1)) 4 //println(list.elementAt(11)) //java.lang.ArrayIndexOutOfBoundsException: 11 5 println(list.elementAtOrElse(10, { 2 * it })) 6 println(list.elementAtOrNull(10)) 7 8 println(list.first { it % 2 == 0 }) 9 //println(list.first { it % 2 == 10 }) //java.util.NoSuchElementException: Collection contains no element matching the predicate 10 println(list.firstOrNull() { it % 2 == 10 }) 11 12 println(list.indexOf(4)) 13 println(list.indexOfFirst { it % 2 == 0 }) 14 println(list.indexOfLast { it % 2 == 0 }) 15 16 println(list.last { it % 2 == 0 }) 17 //println(list.last { it % 2 == 10 }) //java.util.NoSuchElementException: List contains no element matching the predicate 18 println(list.lastIndexOf(5)) 19 println(list.lastOrNull { it % 2 == 10 }) 20 21 println(list.single { it % 6 == 5 }) 22 //println(list.single { it % 2 == 0 }) //java.lang.IllegalArgumentException: Collection contains more than one matching element 23 println(list.singleOrNull() { it % 5 == 10 })
方法作用:
contains--判断集合中是否有指定元素,有则返回true;
elementAt--查找下标对应的元素,如果下标越界会抛IndexOutOfBoundsException异常;
elementAtOrElse--查找下标对应元素,如果越界会根据方法返回默认值(最大下标经方法后的值);
elementAtOrNull--查找下标对应元素,越界会返回Null;
first--返回符合条件的第一个元素,没有则会抛NoSuchElementException异常;
firstOrNull--返回符合条件的第一个元素,没有返回null;
indexOf--返回指定下标的元素,没有返回-1;
indexOfFirst--返回第一个符合条件的元素下标,没有返回-1;
indexOfLast--返回最后一个符合条件的元素下标,没有返回-1;
last--返回符合条件的最后一个元素,没有则会抛NoSuchElementException异常;
lastIndexOf--返回符合条件的最后一个元素,没有返回-1;
lastOrNull--返回符合条件的最后一个元素,没有返回null;
single--返回符合条件的单个元素,如有没有符合的或符合超过一个分别会抛NoSuchElementException或IllegalArgumentException异常;
singleOrNull--返回符合条件的单个元素,如有没有符合或超过一个,返回null;
可以看到,容易出现异常的操作Kotlin会给出另一个安全调用的替代,如first与firstOrNull。
打印结果:
true 1 20 null 0 null 4 0 8 8 5 null 5 null
2.5 生产操作
测试代码:
1 println(list.partition { it % 2 == 0 }) 2 3 println(list + listOf(10, 11)) 4 5 println(list.zip(listOf(7, 8))) 6 println(listOf(Pair(5, 7), Pair(6, 8)).unzip())
方法作用:
partition--根据判断条件是否成立,拆分成两个Pair;
plus--合并两个List,可以用"+"替代;
zip--两个集合按照下标组合成一个个的Pair塞到集合中返回;
unzip--将包含多个Pair的List转换成含List的Pair;
Pair对象的数据组成形式为(first, secord),即Pair(1, 2).first可以取出数据1。
注意:文档和网上一些老的资料还提到了merge操作,编码时提示找不到符号,查资料发现从Kotlin 1.0 Beta 2后的版本开始就弃用了。
打印结果:
([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] [(0, 7), (1, 8)] ([5, 6], [7, 8])
2.6 排序操作
测试代码:
1 println(list.reversed()) 2 3 println(list.sorted()) 4 println(list.sortedBy {it % 3}) 5 6 println(list.sortedDescending()) 7 println(list.sortedByDescending { it % 3 })
方法作用:
reversed--相反顺序;
sorted--自然排序(升序);
sortedBy--根据方法处理结果进行自然(升序)排序;
sortedDescending--降序排序;
sortedByDescending--根据方法处理结果进行降序排序;
注意:新版kotlin需要调用sorted()这样带"ed"后缀的方法才能返回List,而sort()是返回Unit。那么这两种方法还有哪些区别,或者说分别在什么场景下使用?
还是以sort为例,sorted()处理过程中会新建临时的List来保存结果数据,对原来的调用者List不会做任何改变,所以需要将新建的对象返回;而sort()是在原来的List基础上进行元素顺序的调整,不会新建对象,所以不需要返回List。
打印结果:
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 3, 6, 9, 1, 4, 7, 2, 5, 8] [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] [2, 5, 8, 1, 4, 7, 0, 3, 6, 9]
开头部分还定义了一个MutableList对象,下面就结合不带"ed"后缀的排序方法对其进行操作。
1 mutableList.reverse()
2 println(mutableList)
打印输出:
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
如果用list对象调用reverse()会提示List没有该方法,算是各尽其职。而将list打印出来发现果然还是初始化时的顺序:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3. 总结
本文对集合类型List(MutableList针对排序)的总数、过滤、映射、元素、生产及排序六种操作进行了测试,指出了可能出现异常的地方,通过比较加深了List和MutableList这种对应类型的联系与区别。有些操作在Kotlin中只需一句代码就可以得到结果,而在Java中需要手动通过普通的循环或迭代器来对集合中的元素逐一进行处理。
对于Set等其他集合类型,对象创建和操作与List类似,这里不一一举出了。