在 Java
中集合类型也是很重要的数据结构,它可以用来存储一组同类型的数据;
在 Kotlin
中将集合明确分为了可变的集合和不可变的结合,List<out T>
是一个只读的接口,只能执行的 get()
,size()
等读取方法,如果想要更改集合的数据,需要使用 MutableList<T>
,从而区分可辨与不可变集合。Set<out T>
和 MutableSet<T>
、Map<K,out V>
和 MutableMap<K,V>
也是同样的模式。
相比 Java
,Kotlin
中的集合除了包含 Java
中的集合的相关方法,还扩展了很多简化集合操作的函数,结合 Lambda
表达式,把一些对集合的操作变的相当相当方便。
本文主要是对集合类的各种函数进行尝试和整理。
创建和访问
在 Kotlin
中,List<out T>
是只读的,因此只能访问它的 get()
方法,如果需要改变元素的值,需要使用 MutableList<out T>
,Kotlin
中列表的元素可以像数组那样使用 list[index]
的形式访问。
1 | // 创建一个可变列表 |
读写元素
1 |
|
在 Kotlin
中也针对元素的读写扩展了很多简化的方法
1 | // index 在范围内返回值,否则返回 lambda 表达式的结果 |
获取下标
1 | // 该元素出现的最后一次的下标 |
查找第一个满足条件的元素
1 | // 查找第一个满足条件的,找不到就抛异常 |
从后向前查找第一个满足条件的元素
1 | // 从后向前查找第一个满足条件的,找不到就抛异常 |
遍历
集合的遍历是个老话题,Kotlin
中集合的遍历更优雅,更强大。
forEach 循环
1 | // 只关注 value |
迭代器遍历,相当于使用 Java
中的 Iterate
进行遍历。
1 | // 迭代器遍历 |
for…in 循环
1 | // 遍历下标 |
遍历一个列表时,如果该列表是基于数组结构的,那么会具有数组的特点,访问容易但是插入相对困难,如果列表是基于链表结构的,那么会具有链表的特点,访问困难但是插入相对容易;遍历数组时可以根据此特点进行优化。
1 | if (list is RandomAccess) { |
检测元素
这部分函数主要用来简化我们需要遍历集合得到一个 boolean
结果的情况,在 Java
中我们通常需要写一个循环然后挨个判断每个元素达到结果。在 Kotlin
中借助 Lambda
表达式,我们可以像操作一个整形变量一样操作集合。
1 | // 如果全都不满足条件返回 true |
filter 操作
列表的 filter
操作是指按照一定的条件对列表元素进行过滤,返回包含过滤后元素的新列表。
在进行这部分函数设计时,总会有 xxxx()
和 xxxxTo(list:MutableCollection<T>)
这样的函数组合,前者将会返回一个 List<out T>
类型的集合,也就是只读的,而后者将会返回参数中传递的 MutableCollection<T>
,他是可变的,下面将不再赘述。
1 | val destList = mutableListOf<String>() |
带有下标的的过滤
1 | // 与 filter 函数相似,但是函数中会有 index 参数,返回 List<out T> |
过滤属于某个类型的元素
1 | // 使用 reified 关键字,因此不需要使用 class |
filterNot
1 | // 过滤不满足的条件的元素,与 filter 函数的功能相反,存储到新的列表中 |
搜索
从列表中按照条件搜索元素
1 | // 查找第一个满足条件的值,返回 T?类型,查找不到将会为 null |
二分搜索,使用 binarySearch
之前,列表应被排为升序,否则结果是不确定的
1 | // 根据传入的 lambda 表达式参数判断查找的值是否合适,返回负数表示,查找到的值过小,正数表示查找到的值过大,0 表示查找到 |
排序
对于 List<out T>
类型来说,排序之后仍然只能返回只读列表,注意,返回的列表将是一个新的列表,与原来的列表没有关系,为啥呢?因为原来的列表是不可变的啊。
1 | // 范型 T 对应的类型要是 Comparable 的子类 |
对于 MutableCollection<T>
类型来说,内部使用的是 Collection.sort()
方法来排序,这是一个在 java
中就使用的方法,函数的返回值是 Unit
,这意味着函数不会返回一个新的列表,排序后的列表与原来的列表是同一个,当然他也可以使用上面的函数排序返回一个不可变列表,但是一个不可变列表是无法使用下面的函数排序的。
函数声明与上面的函数类似,但是没有 ed
后缀。
1 | list.sort() |
删除
一般来说,List<out T>
类型是不可变的,但是可以使用 drop()
函数从列表中删除元素,但是此时删除后返回的列表是一个全新的列表。
1 | // 去除前 n 个返回列表 |
对于可变列表,可以使用 remove()
函数删除元素
1 | // 删除元素,返回是否删除成功 |
转换
类似 RxJava
中的操作符,使用操作符可以将列表转换为其他类型。主要有 map
,flapMap
,associate
等。
map
使用 Lambda
表达式将每一项 map
到新的数据
1 | // 返回新的 List<out Int> |
使用 mapNotNull
将集合中的非空数据进行 map
,与上面的函数是类似的。
1 | list.mapNotNull { it.length } |
flatmap
使用 flatmap
对集合中每一项都转换成一个集合,最后在一个存储在一个集合中。
1 | // 对每一项映射返回一个集合 |
associate
这个操作主要用来将列表转换成对应的 Map
。
以 Pair
的形式返回键值对,将 List<T>
类型转换为 Map<K,V>
,返回的 Map
类型取决于返回 Pair
类型。
1 | list.associate { Pair(it, it.length) } |
只接受一个 keySelector
参数用来映射生成最终 Map
的 key
,将 List<T>
类型转换为 Map<K,T>
1 | list.associateBy { it } |
接受一个 keySelector
和 valueTransform
作为参数,映射生成 key-value
,将 List<T>
类型转换为 Map<K,V>
;
1 | list.associateBy(keySelector = { it }, valueTransform = { it.length }) |
最大值最小值
用于查找整个列表中的最大值最小值
1 | // 范型对应的的类 需要是 Comparable 的子类 |
运算符
列表支持直接使用运算符操作;
从列表中减去一个元素,或者加上一个元素,我们可以使用如下函数
1 | // 函数,减法操作 |
也有相同功能的 operator
函数,返回值为一个新的 List<out T>
1 | // operator,减法,接受一个元素 |
由于上面的函数使用了 operator
注解标记,我们可以直接使用 +
和 -
代替使用函数。
1 | val a = list + "1" |
同样,可变列表也支持 +=
和 -=
运算符,但是不可变列表无法使用这些函数,操作符对应的函数如下,返回值为 Unit
,新的更改将作用于当前的列表。
1 | // operator,-= 操作,接受一个元素 |
也可以直接使用 +=
和 -=
运算符
1 | list += "1" |
累积
累积是我自己起的名字,是说遍历整个列表做一个累积操作,相关的函数有 sum
,reduce
,fold
等。
sum
函数 sum()
是对列表每一项使用 Lambda
表达式转换为数字类型数据然后求和的操作;
1 | // 对列表求和,返回 int |
reduce
函数 reduce()
将列表的第一项作为初始值,然后对列表每一项使用 Lambda
表达式转换,然后累积,由于使用了列表的第一项作为初始值,所以 reduce()
的返回值与列表范型类型相同。
下面函数中 index
表示元素下标,acc
表示累积的结果,next
表示下一个元素。
1 | list.reduce { |
fold
函数 fold()
与 reduce()
有些类似,但是 fold()
函数不是以列表的第一项作为初始值进行累积,而是可以指定一个初始值,也就是说,fold()
函数可以返回任意类型的累积结果。
1 | list.fold(10) { |
take
使用 take()
函数可以从列表中取出指定的部分值组成新的列表
1 | // 返回此时的列表的前 n 项,新的列表 |
分组和切片
对列表进行分组和切片操作。
使用 group()
函数对列表分组,返回结果是 Map<K,List<T>>
类型,key
的类型由 Lambda
表达式的返回值决定。
1 | list.groupBy { |
使用 partition()
函数,根据 Lambda
表达式返回 true/false
将列表分成两个列表,返回值为 Pair<List<T>,List<T>>
1 | list.partition { it.length > 10 } |
对列表进行切片,返回切片后的新列表
1 | list.subList(0, 2) |
其他
另外一些无法归类的函数
使用 contains()
函数判断列表是否包含某个或某些元素
1 | list.contains("") |
判空
1 | // 如果 list 为空,返回一个新列表 |
使用 reverse()
函数反转列表,不可变列表可以使用 asReversed()
函数,这个函数将返回一个反转后的新的列表;
可变列表使用 reverse()
函数,返回值为 Unit
,列表的反转将作用在自己身上,内部使用 Java
的 Collection. reverse()
实现,不可变列表不能调用该函数。
1 | list.reverse() |
使用 distinct()
对列表进行去重
1 | list.distinct() |
使用 count()
函数,获取列表长度
1 | // 元素个数 |
使用 replaceAll()
函数替换全部数据
1 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
使用 join()
函数,对每一项进行拼接,接受一个 Appendable
对象作为参数,可以设置拼接的前缀,后缀,分隔符等
1 | list.joinTo(StringBuilder(), "-", "#", "*", 4) { |
使用 single()
函数,当列表只有一个元素时获取这个元素
1 | // 当只有一个元素时返回该元素,否则异常 |
使用 union()
函数,返回两个列表的非重复集合
1 | list.union(destList) |
使用 zip()
函数压合两个列表,返回值为 List<Pair<T,R>>
,列表长度等于较短的一个
1 | val list2 = listOf(4, 5, 6) |
使用 subtract()
函数从列表中剔除部分元素
1 | list.subtract(destList) |
使用 retainAll()
函数对两个列表取交集。
1 | list.retainAll(destList) |