文章

Kotlin高级——集合

开始

kotlin将Map、Set和List都分为只读和可变两种类型,只读类型使用原始名称,可变类型带有Mutable前缀,可变类型在只读类型的基础上扩展了读写接口

容器类图如下

Collection interfaces hierarchy

容器默认实现

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

本文由作者按照 CC BY 4.0 进行授权