Kotlin 集合框架

集合概述

Kotlin 标准库提供了一整套用于管理集合的工具,集合是可变数量(可能为零)的一组条目,各种集合对于解决问题都具有重要意义,并且经常用到。

集合通常包含相同类型的一些(数目也可以为零)对象。集合中的对象称为元素条目。例如,一个系的所有学生组成一个集合,可以用于计算他们的平均年龄。 以下是 Kotlin 相关的集合类型:

  • List 是一个有序集合,可通过索引(反映元素位置的整数)访问元素。元素可以在 list 中出现多次。列表的一个示例是一句话:有一组字、这些字的顺序很重要并且字可以重复。
  • Set 是唯一元素的集合。它反映了集合(set)的数学抽象:一组无重复的对象。一般来说 set 中元素的顺序并不重要。例如,字母表是字母的集合(set)。
  • Map(或者字典)是一组键值对。键是唯一的,每个键都刚好映射到一个值。值可以重复。map 对于存储对象之间的逻辑连接非常有用,例如,员工的 ID 与员工的位置。

Kotlin 让你可以独立于所存储对象的确切类型来操作集合。换句话说,将 String 添加到 String list 中的方式与添加 Int 或者用户自定义类的到相应 list 中的方式相同。 因此,Kotlin 标准库为创建、填充、管理任何类型的集合提供了泛型的(通用的,双关)接口、类与函数。

这些集合接口与相关函数位于 kotlin.collections 包中。

集合类型

Kotlin 标准库提供了基本集合类型的实现: set、list 以及 map。 一对接口代表每种集合类型:

  • 一个 只读 接口,提供访问集合元素的操作。
  • 一个 可变 接口,通过写操作扩展相应的只读接口:添加、删除和更新其元素。
val numbers = mutableListOf("one", "two", "three", "four")
numbers.add("five")   // 这是可以的
//numbers = mutableListOf("six", "seven")      // 编译错误

 更改可变集合不需要它是以 var 定义的变量:写操作修改同一个可变集合对象,因此引用不会改变。 但是,如果尝试对 val 集合重新赋值,你将收到编译错误。

集合的型变

集合层次结构

与Java对比,有什么不同

对比Java,Kotlin只是增加了"不可变"集合框架的接口,没有另起炉灶,复用Java API的所有实现类型。

提供了丰富易用的方法,例如forEach/map/flatMap。Scala也是一门JVM语言,Kotlin很多特性都参考了Scala。

运算符级别的支持,简化集合框架的访问。

Java中的类型

Kotlin的集合创建

有自己的类型,所以是"一等公民",可以赋值、传递,并在合适的条件下调用。 以下是各种函数的用法,关键点都写在注释当中了:

创建集合的最常用方法是使用标准库函数:listOf<T>()、setOf<T>()、mutableListOf<T>()、mutableSetOf<T>()​。

如果以逗号分隔的集合元素列表作为参数,编译器会自动检测元素类型。创建空集合时,须明确指定类型。

List集合的创建

// 这两个编译完成后都变成了java里面的List<T>
val intList1: List<Int> = listOf(1,2,3) //不可变
val intList2: MutableList<Int> = mutableListOf(1,2,3)  //可变

val intList4  = listOf(1,2,3)
val intList5 = mutableListOf(1,2,3)
    
// 创建空集合的时候需要指定类型
val intList6 = mutableListOf<Int>()


// 对于 List,有一个接受 List 的大小与初始化函数的构造函数,该初始化函数根据索引定义元素的值。
val doubled1 = List(3, { it * 2 })
println(doubled1) // 输出结果: [0, 2, 4]

// 如果你想操作这个集合,应使用 MutableList
val doubled2 = MutableList(3, { it * 2 })
println(doubled2) // 输出结果: [0, 2, 4]

 Map集合的创建

// "name" to "oLiver" 可以理解为K—V键值对
// Any等价于java中的Object
val map1: Map<String , Any> = mapOf("name" to "oLiver", "age" to 30)
val map2: MutableMap<String , Any> = mutableMapOf("name" to "oLiver", "age" to 30)

val map4 = mapOf("name" to "oLiver", "age" to 30)
val map5 = mutableMapOf("name" to "oLiver", "age" to 30)

// 创建空集合的时候需要指定类型
val map6 = mutableMapOf<String, Int>()

 Set集合的创建

val set1: Set<String> = setOf("one", "two", "three", "four")
val set2: MutableSet<String> = mutableSetOf("one", "two", "three", "four")

val numbersSet1 = setOf("one", "two", "three", "four")
val numbersSet2 = mutableSetOf("one", "two", "three", "four")

// 创建空集合的时候需要指定类型
val emptySet = mutableSetOf<String>()

快捷方式创建集合

// 快捷方式创建:ArrayList<T> = java.util.ArrayList<E>
// ArrayList<T> 叫做类型别名,他是指向于java相对于的工具类
// 包括HashMap,HashSet,LinkedHashMap,LinkedHashSet等都是类型别名
val stringList = ArrayList<String>()

复制集合

浅复制

要创建与现有集合具有相同元素的集合,可以使用复制操作。标准库中的集合复制操作创建了具有相同元素引用的  复制集合。 因此,对集合元素所做的更改会反映在其所有副本中。

val sourceList = mutableListOf(1, 2, 3)
val referenceList = sourceList
referenceList.add(4)
println(sourceList.joinToString())  // 输出结果:1, 2, 3, 4
println(referenceList.joinToString()) // 输出结果:1, 2, 3, 4

集合的初始化可用于限制其可变性。例如,如果构建了一个 MutableList 的 List 引用,当你试图通过此引用修改集合的时候,编译器会抛出错误。

val sourceList = mutableListOf(1, 2, 3)
val referenceList: List<Int> = sourceList
//referenceList.add(4)            // 编译错误
sourceList.add(4)
println(referenceList.joinToString()) // 显示 sourceList 当前状态, 输出结果:1, 2, 3, 4
println(sourceList.joinToString()) // 显示 sourceList 当前状态, 输出结果:1, 2, 3, 4

使用复制函数

在特定时刻通过集合复制函数,例如toList()、toMutableList()、toSet() 等等。创建了集合的快照。 结果是创建了一个具有相同元素的新集合 如果在源集合中添加或删除元素,则不会影响副本。副本也可以独立于源集合进行更改。

val sourceList = mutableListOf(1, 2, 3)
val copyList = sourceList.toMutableList()
val readOnlyCopyList = sourceList.toList()
sourceList.add(4)
println("Copy: ${copyList.joinToString()}") // Copy: 1, 2, 3
println("sourceList: ${sourceList.joinToString()}") // sourceList: 1, 2, 3, 4
println("readOnlyCopyList: ${readOnlyCopyList.joinToString()}") // readOnlyCopyList: 1, 2, 3

//readOnlyCopyList.add(4)             // 编译异常
println("Read-only copy size: ${readOnlyCopyList.size}")

这些函数还可用于将集合转换为其他类型,例如根据 List 构建 Set,反之亦然。

val sourceList = mutableListOf(1, 2, 3)
val copySet = sourceList.toMutableSet()
copySet.add(3)
copySet.add(4)
println(copySet.joinToString()) // 输出结果:1, 2, 3, 4

Kotlin的集合读写

  • 添加元素
val intList3: MutableList<Int> = mutableListOf() //不可变
for (i in 0 .. 10) {
    intList3.add(i) // 往后添加
}
println(intList3) // 输出结果:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
val stringList2 = ArrayList<String>()
val stringList3 = ArrayList<String>()
for (i in 0 .. 10) {
    stringList2.add("num:$i")
    stringList3 += "num:$i" // 运算符可以重载,这里 += 的用途相当于add
}
println(stringList2) // 输出结果:[num:0, num:1, num:2, num:3, num:4, num:5, num:6, num:7, num:8, num:9, num:10]
println(stringList3) // 输出结果:[num:0, num:1, num:2, num:3, num:4, num:5, num:6, num:7, num:8, num:9, num:10]
  • 删除元素
val stringList3 = ArrayList<String>()
for (i in 0 .. 10) {
    stringList3 += "num:$i" // 运算符可以重载,这里 += 的用途相当于add
}

println(stringList3) // 输出结果:[num:0, num:1, num:2, num:3, num:4, num:5, num:6, num:7, num:8, num:9, num:10]

stringList3.remove("num:2")
println(stringList3) // 输出结果是:[num:0, num:1, num:3, num:4, num:5, num:6, num:7, num:8, num:9, num:10]
stringList3 -= "num:6"
println(stringList3) // 输出结果:[num:0, num:1, num:3, num:4, num:5, num:7, num:8, num:9, num:10]
  • 访问元素,修改某个元素的值
val stringList3 = ArrayList<String>()
for (i in 0 .. 10) {
    stringList3 += "num:$i" // 运算符可以重载,这里 += 的用途相当于add
}
println(stringList3) // 输出结果:[num:0, num:1, num:2, num:3, num:4, num:5, num:6, num:7, num:8, num:9, num:10]

stringList3[0] = "HelloWorld" // 也可以根据角标来修改对应角标的值
val valueAt5 = stringList3[0]
println(valueAt5) // 输出结果:HelloWorld
println(stringList3)
  • Map集合的元素访问
val map2: MutableMap<String , Any> = mutableMapOf("name" to "oLiver", "age" to 30)
val map3 = HashMap<String, Int>()

println(map2) // 输出结果:{name=oLiver, age=30}
println(map3) // 输出结果:{}
map2["age"] = 10
map3["age"] = 10
println(map2) // 输出结果:{name=oLiver, age=10}
println(map3) // 输出结果:{age=10}
println(map3["age"]) // 输出结果:10

集合的遍历

对于遍历集合元素, Kotlin 标准库支持 迭代器 的常用机制——对象可按顺序提供对元素的访问权限,而不会暴露集合的底层结构。 当需要逐个处理集合的所有元素(例如打印值或对其进行类似更新)时,迭代器非常有用。

Iterable<T> 接口的继承者(包括 Set 与 List)可以通过调用 iterator() 函数获得迭代器。 一旦获得迭代器它就指向集合的第一个元素;调用 next() 函数将返回此元素,并将迭代器指向下一个元素(如果下一个元素存在)。 一旦迭代器通过了最后一个元素,它就不能再用于检索元素;也无法重新指向到以前的任何位置。要再次遍历集合,请创建一个新的迭代器。

val numbers = listOf("one", "two", "three", "four")
val numbersIterator = numbers.iterator()
while (numbersIterator.hasNext()) {
    println(numbersIterator.next())
}

遍历 Iterable 集合的另一种方法是众所周知的 for 循环。在集合中使用 for 循环时,将隐式获取迭代器。因此,以下代码与上面的示例等效:

val numbers = listOf("one", "two", "three", "four")
for (item in numbers) {
    println(item)
}

最后,有一个好用的 forEach() 函数,可自动迭代集合并为每个元素执行给定的代码。因此,等效的示例如下所示:

val numbers = listOf("one", "two", "three", "four")
numbers.forEach {
    println(it)
}

List 迭代器

对于列表,有一个特殊的迭代器实现: ListIterator 它支持列表双向迭代:正向与反向。 反向迭代由 hasPrevious() 和 previous() 函数实现。 此外, ListIterator 通过 nextIndex() 与 previousIndex() 函数提供有关元素索引的信息。

具有双向迭代的能力意味着 ListIterator 在到达最后一个元素后仍可以使用。

val numbers = listOf("one", "two", "three", "four")
val listIterator = numbers.listIterator()
while (listIterator.hasNext()) listIterator.next()
println("Iterating backwards:")
while (listIterator.hasPrevious()) {
    print("Index: ${listIterator.previousIndex()}")
    println(", value: ${listIterator.previous()}")
}

/**
 * Iterating backwards:
    Index: 3, value: four
    Index: 2, value: three
    Index: 1, value: two
    Index: 0, value: one
 * */

可变迭代器

为了迭代可变集合,于是有了 MutableIterator 来扩展 Iterator 使其具有元素删除函数 remove() 。因此,可以在迭代时从集合中删除元素。

val numbers = mutableListOf("one", "two", "three", "four") 
val mutableIterator = numbers.iterator()

mutableIterator.next()
mutableIterator.remove()    
println("After removal: $numbers") // After removal: [two, three, four]

除了删除元素, MutableListIterator 还可以在迭代列表时插入和替换元素。

val numbers = mutableListOf("one", "four", "four") 
val mutableListIterator = numbers.listIterator()

mutableListIterator.next()
mutableListIterator.add("two")
mutableListIterator.next()
mutableListIterator.set("three")   
println(numbers)  // [one, two, three, four]

集合的包含关系(类似数组和区间)

Pair & Triple

Pair,就是对,键值对的意思,包含两个元素,Triple则包含三个元素

Pair 创建键值和获取,解构

// 创建键值对的两种方式
val pair1 = "key" to "value"
val pair2 = Pair("key", "value")
println(pair1) // 输出结果:(key, value)
println(pair2) // 输出结果:(key, value)

//获取对应的元素,第一个和第二个
val first = pair1.first
val second = pair1.second
println(first) // 输出结果:key
println(second) // 输出结果:value

// 解构,创建一个x,y的解构,来存储键值对
val (x, y) = pair2
println(x) // 输出结果:key
println(y) // 输出结果:value

Triple 创建键值和获取,解构

// Triple 包含三个元素, 使用方法和Pair一样
val triple = Triple("key", 2, 3.0)
val first = triple.first
val second = triple.second
val third = triple.third
val (x, y, z) = triple
println(first) // 输出结果:key
println(second) // 输出结果:2
println(third) // 输出结果:3.0
println(x) // 输出结果:key
println(y) // 输出结果:2
println(z) // 输出结果:3.0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值