Kotlin高级——集合
开始
kotlin将Map、Set和List都分为只读和可变两种类型,只读类型使用原始名称,可变类型带有Mutable前缀,可变类型在只读类型的基础上扩展了读写接口
容器类图如下
容器默认实现
- MutableList:ArrayList
- MutableSet:LinkedHashSet
- MutableMap:LinkedHashMap
自定义集合:kotlin提供了每种集合的抽象类,用于自定义集合实现
- AbstractCollection
- AbstractList
- AbstractSet
- AbstractMap
元组容器
Pair和Triple是kotlin内置的元组对象,Pair是二元组,Triple是三元组
Pair和Triple都包含toXXX方法,将元组转换为其他数据类型
Pair对于任意类型扩展了to方法,A.to(B)
返回一个Pair<A, B>
对象,to方法使用infix修饰,可以使用中缀表示
集合创建
集合创建方法
调用XXXOf函数:传入元素序列,不传入时创建空集合
1 2 3
val l = listOf(1, 2, 3, 4) val s = setOf(1, 2, 3, 4) val m = mapOf(1 to 2, 3 to 4) // map接收pair类型
listOf
:使用默认实现,自动类型推断,当存在null时,返回可空类型,不存在时,返回非空类型mutableListOf
:listOf的可变类型listOfNotNull
:使用默认实现,自动类型推断,可接收可空类型,自动过滤null,返回非空类型arrayListOf
:使用具体实现,其余特性与listOf一致
调用buildXXX函数:传入一个闭包,可初始化容量
闭包接收者为集合的可变类型,通过可变类型修改容器,修改越界时会抛出异常
调用emptyXXX:创建空集合
调用具体实现的构造函数
集合转换、复制
集合转换、复制方法
List和Set转换
List和Set调用toXXX可以任意地相互转换到它们的只读类型或可变类型,当list转换为set时会去除重复元素
map转换
List和Set无法直接转换为Map,Map只能转换为List,其中的元素为Pair对象
调用
map.keys
可以获取由key组成的set集合,调用map.values
可以获取由value组成的Collection集合转换拷贝
在调用toXXX进行转换时实际是创建了一个新对象且对元素进行浅拷贝,调用toXXX方法时可以调用同类型的toXXX方法,因此可以通过该方法实现拷贝
引用可变限制:由于可变类型是只读类型的子类,当可变类型集合被赋值给只读类型引用时,集合变为只读,通过可变类型引用也不能修改集合,此时只能将对象强转为可变类型
迭代器
可迭代对象接口为Iterable<T>
,包含一个iterator方法返回一个Iterator对象
迭代器接口为Iterator<T>
,拥有两个方法
- hasNext:判断是否有下一个元素
- next:访问下一个元素
对于List还实现了一个Iterator的子接口ListIterator,添加了方法实现反向迭代
- hasPrevious:判断是否有上一个元素
- previous:访问上一个元素
- nextIndex:返回当前元素的下一个索引
- previousIndex:返回当前元素的前一个索引
可变迭代器
对于可变类型,实现了一个MutableIterator子接口,包含一个remove方法,可以在迭代时删除元素
对于List还实现了MutableListIterator子接口,添加了List的修改和添加方法
1
2
3
4
5
val s = mutableSetOf(2, 3, 4)
val iterator = s.iterator()
iterator.next() // 到达第一个元素
iterator.remove() // 删除2,[3, 4]
iterator.next() // 保持相对位置,next返回3
序列
序列是kotlin提供的另一种容器,用于集合流处理,类似java的Stream
序列与集合的不同
- 集合是立即计算,在调用操作函数后立即计算返回结果,序列是惰性计算,调用操作后将操作记录下来,在调用终止操作时才进行计算
- 集合每一步计算都会创建新的对象,序列将所有步骤一次性执行,不会创建集合的副本中间对象
- 序列不支持集合的随机访问,序列是只读的,不支持元素修改和添加删除
- 集合的执行流是将一个操作对所有元素执行一次,序列的执行流是对一个元素执行一次所有操作
序列创建
调用sequenceOf函数,传入元素序列
调用集合的asSequence函数,类似java中的stream方法
调用generateSequence函数,传入一个闭包作为元素生成函数,当生成函数返回null时,生成结束
调用sequence函数,传入一个闭包,闭包中只能调用yield和yieldAll函数,用于生成元素
与generateSequence的不同:generateSequence是将序列创建好后进入流操作,sequence函数是从生成元素就进入流操作,当操作下一个元素时generateSequence不会被调用,而sequence会被调用,由yield产生下一个操作的值,产生后sequence会被暂停
元素生成方法
- yield:生成一个值
- yieldAll:生成一个序列,传入集合或序列
集合操作
集合的操作函数以成员函数或扩展函数存在,集合操作分为两类,公共操作和写操作,公共操作可用于只读集合和可变集合,写操作只能用于可变集合
集合与序列在操作时的不同
- 对于集合,会创建集合的副本对象,因此当集合操作分步执行时,需要对返回的新对象进行操作
- 对于序列,会创建只记录操作信息的对象,可对原来操作的对象继续操作
1
2
3
4
5
6
7
val list = listOf(1, 2, 3, 4)
val filter = list.filter { it > 2 } // 操作返回一个新的对象
fileter.forEach { println(it) } // 对新对象继续操作
// 错误写法
val l = listOf(1, 2, 3, 4)
l.fileter { it > 2 } // 操作返回的对象被丢弃
l.forEach { println(it) } // 对原来的list进行操作,filter没有执行
集合操作详情见官方文档:集合操作概述 · Kotlin 官方文档 中文版 (kotlincn.net)