Kotlin入门第二课:集合操作

测试项目Github地址:
KotlinForJava

前文传送:
Kotlin入门第一课:从对比Java开始
初次尝试用Kotlin实现Android项目

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对象,后面的操作会用到。

val list = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
val mutableList = mutableListOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

2.1 总数操作

测试代码:

println(list.any { it % 2 == 1 })

println(list.all { it % 2 == 1 })

println(list.count { it % 2 == 1 })

println(list.fold(10) { total, next -> total + next })
println(list.foldRight(10) { total, next -> total + next })

list.forEach { value -> if (value > 8) println(value) }
list.forEachIndexed { index, value -> if (value > 8) println("value of index $index is $value") }

println(list.max())
println(list.maxBy { -it })

println(list.min())
println(list.minBy { -it })

println(list.none { it % 2 == 10 })

println(list.reduce { total, next -> total + next })
println(list.reduceRight { total, next -> total + next })

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 过滤操作

测试代码:

println(list.drop(4))
println(list.dropWhile { it < 9 })
println(list.dropLastWhile { it < 9 })

println(list.filter { it % 2 == 0 })
println(list.filterNot { it % 2 == 0 })
println(list.filterNotNull())

println(list.slice(listOf(0, 4, 8)))
//println(list.slice(listOf(0, 4, 80)))  //java.lang.ArrayIndexOutOfBoundsException: 80

println(list.take(2))
println(list.takeLast(2))
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 映射操作

测试代码:

println(list.flatMap { listOf(it, it + 1) })

println(list.groupBy { if (it % 2 == 0) "even" else "odd" })

println(list.map { it * 2 })
println(list.mapIndexed { index, it -> index * it })
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 元素操作

测试代码:

println(list.contains(2))

println(list.elementAt(1))
//println(list.elementAt(11))  //java.lang.ArrayIndexOutOfBoundsException: 11
println(list.elementAtOrElse(10, { 2 * it }))
println(list.elementAtOrNull(10))

println(list.first { it % 2 == 0 })
//println(list.first { it % 2 == 10 })  //java.util.NoSuchElementException: Collection contains no element matching the predicate
println(list.firstOrNull() { it % 2 == 10 })

println(list.indexOf(4))
println(list.indexOfFirst { it % 2 == 0 })
println(list.indexOfLast { it % 2 == 0 })

println(list.last { it % 2 == 0 })
//println(list.last { it % 2 == 10 })  //java.util.NoSuchElementException: List contains no element matching the predicate
println(list.lastIndexOf(5))
println(list.lastOrNull { it % 2 == 10 })

println(list.single { it % 6 == 5 })
//println(list.single { it % 2 == 0 })  //java.lang.IllegalArgumentException: Collection contains more than one matching element
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 生产操作

测试代码:

println(list.partition { it % 2 == 0 })

println(list + listOf(10, 11))

println(list.zip(listOf(7, 8)))
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 排序操作

测试代码:

println(list.reversed())

println(list.sorted())
println(list.sortedBy {it % 3})

println(list.sortedDescending())
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"后缀的排序方法对其进行操作。

mutableList.reverse()
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类似,这里不一一举出了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容