# Kotlin教程 - 7 容器

什么是容器?

容器就是可以存储多个数据的一种数据类型,容器内的每一个数据称之为元素,其中的元素可以是任意类型的数据,包括字符串、数字、布尔,甚至是容器等等。

例如有一个班级,有40个学生,我们肯定不能定义40个变量来存储学生的信息,有1000个人,那不要命了,我们可以将所有学生的信息放到容器中。

在 Kotlin 中常用的容器分为4类:

  • 数组(Array)
  • 列表(List)
  • 集合(Set)
  • 字典(Map)

不同的容器有不同的特点,例如:

  • 是否支持重复的元素,有的容器中的元素不能重复,有的可以。
  • 是否有序

下面一一介绍。

# 7.1 数组

数组是一种有序的容器,用于存储相同类型的元素。

数组的主要特点:

  • 有序
  • 只能存储相同类型的元素
  • 大小在创建的时候就确定了,大小不可变

# 1 创建数组

方式1:arrayOf

fun main() {
    // 创建一个用于存储整数的数组,并存储了1、2、3、4、5
    val numberArray1: Array<Int> = arrayOf(1, 2, 3, 4, 5)
    // 类型也可以省略
    val numberArray2 = arrayOf(1, 2, 3, 4, 5)
}
1
2
3
4
5
6

方式2:创建空组数

fun main() {
    // 创建一个空数组,长度为5,后面可以修改数组中的值
    val emptyArray = arrayOfNulls<String>(5)
}
1
2
3
4

方式3:数组构造函数

fun main() {
    // 创建一个整形数组,长度为5,其中的元素都是0
    val numberArray = Array(5) { 0 }

    // 创建一个字符串类型数组,长度为5,其中的元素都是空字符串
    val stringArray = Array(5) { "" }

    // 创建一个可空的字符串数组,长度为5,其中的元素都是null
    val nullableArray = Array<String?>(5) { null }

    // 创建一个整数数组,长度为5,并将元素初始化为索引的两倍,则其中的元素为[0, 2, 4, 6, 8]
    val numberArray2 = Array(5) { index -> index * 2 }

    // 创建一个字符串数组,长度为5,其中的元素使用when表达式通过index进行设置,最后的结果["Hello","old","are","you","!"]
    val stringArray2 = Array(5) { index ->
        when (index) {
            0 -> "Hello"
            1 -> "old"
            2 -> "are"
            3 -> "you"
            4 -> "!"
            else -> throw IndexOutOfBoundsException("数组索引越界")   // 异常后面再讲
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

使用数组构造函数创建,还可以使用 lambda 表达式,根据 index 来设置元素。

方式4:使用原生整数数组类型

以下以 IntArray 为例, IntArray 是一个整形数组,与泛型数组 Array<Int> 不同,IntArray 是一个特定于整数的数组类型。

举个栗子:

fun main() {
    // 使用IntArray构造函数初始化,长度为5,初始值为 0 的数组
    val numbers1 = IntArray(5) 

    // 使用 intArrayOf 函数 创建一个包含指定元素的数组
    val numbers2 = intArrayOf(1, 2, 3, 4, 5)
}
1
2
3
4
5
6
7

Kotlin 中除了 IntArray,还有一些其他的原生数组类型,以及泛型数组类型。以下是一些常见的数组类型:

  • IntArray:用于存储整数。
  • DoubleArray:用于存储双精度浮点数。
  • FloatArray:用于存储单精度浮点数。
  • LongArray:用于存储长整数。
  • ShortArray:用于存储短整数。
  • ByteArray:用于存储字节。
  • CharArray:用于存储字符。
  • BooleanArray:用于存储布尔值。

这些原生数组类型直接使用底层的原生数据类型,因此具有高效的性能。

# 2 基础操作

# 获取长度

使用 size() 方法可以获取到数组的长度。

fun main() {
    val numbers: Array<Int> = arrayOf(1, 2, 3, 4, 5)
    println("数组长度:" + numbers.size)    // 输出:5
}
1
2
3
4

# 访问元素

可以使用下标来访问列表中的元素或修改数组中的元素,从0开始,注意不要越界,下标越界会报错。

fun main() {
    val numbers: Array<Int> = arrayOf(1, 2, 3, 4, 5)
    var num = numbers[0]      // 获取第一个元素
    println(num)      				// 输出:1

    num = numbers[numbers.lastIndex]     // 获取最后一个元素
    println(num)      				// 输出:5

    numbers[0] = 100        	// 修改元素
    println(numbers[0])
}
1
2
3
4
5
6
7
8
9
10
11

因为数组的长度创建后就无法更改了,所以我们无法删除和添加更多到元素到数组中,只能修改数组中的元素。

除了使用 [index] 的方式访问元素,还可以使用 get 函数来访问。

举个栗子:

fun main() {
    val numbers: Array<Int> = arrayOf(1, 2, 3, 4, 5)
    val num1 = numbers.get(0)       				// 获取第一个元素
    val num6 = numbers.getOrElse(6) { 0 }   // 获取index为6的元素
    val num7 = numbers.getOrNull(7) ?: 0    // 获取index为7的元素
}
1
2
3
4
5
6
  • 可以使用 get(index) 函数获取指定 index 的元素,如果 index 大于数组长度,或报错;
  • 可以使用 getOrElse(Index) 获取指定 index 的元素,如果越界,则返回 lambda 表达式的结果;
  • 可以使用 getOrNull(Index) 获取指定 index 的元素,如果越界,则返回 null,在上面的代码中,我们结合 ?: 操作符,如果为 null ,就赋 0 。

Kotlin 还提供了一些获取指定位置元素的函数。

举个栗子:

fun main() {
    val numbers: Array<Int> = arrayOf(1, 2, 3, 4, 5)
    val firstElement = numbers.first()  // 获取第一个额原始
    val lastElement = numbers.last()    // 获取最后一个元素
}
1
2
3
4
5

# 3 遍历数组

使用 for-in 循环

可以使用 for-in 循环来遍历数组的元素。这适用于所有类型的数组,包括原生数组和泛型数组。

fun main() {
    val colors = arrayOf("Red", "Blue", "Green")

    for (color in colors) {   // 依次取出数组中的每个元素
        println(color)
    }
}
1
2
3
4
5
6
7

使用索引和 indices 属性

如果需要访问数组的索引以及元素,可以使用数组的 indices 属性,然后通过索引来访问元素。

fun main() {
    val colors = arrayOf("Red", "Blue", "Green")

    for (index in colors.indices) {     // index 每个元素的索引
        println("Index: $index, Element: ${colors[index]}")     // 通过索引取出每个元素
    }
}
1
2
3
4
5
6
7

使用 forEach 函数

Kotlin 提供了 forEach 函数,可用于遍历数组元素。这对于处理集合更加方便。

fun main() {
    val colors = arrayOf("Red", "Blue", "Green")

    colors.forEach { element ->
        println(element)           // 依次取出数组中的元素
    }
}
1
2
3
4
5
6
7

使用 forEachIndexed 函数

如果需要同时访问索引和元素,可以使用 forEachIndexed 函数。

fun main() {
    val colors = arrayOf("Red", "Blue", "Green")

    colors.forEachIndexed { index, element ->
        println("Index: $index, Element: $element")
    }
}
1
2
3
4
5
6
7

使用迭代器

还可以通过获取数组的迭代器来遍历元素。

fun main() {
    val colors = arrayOf("Red", "Blue", "Green")
    val iterator = colors.iterator()    // 获取数组的迭代器对象,通过迭代器对象取出数组中的元素
    while (iterator.hasNext()) {        // 判断是否有下一个元素
        val color = iterator.next()     // 取出下一个元素
        println(color)
    }
}
1
2
3
4
5
6
7
8

# 7.2 List

List 也是一个有序的容器,和数字很像,但是它的长度是可以变化的,所以我们可以随时添加和删除其中的元素,List主要有如下特点:

  • 列表中的元素可以重复
  • 列表中的元素可以修改,可以增加、修改、删除
  • 列表中的元素是有序的,可以通过索引来访问
  • 列表中可以存储不同数据类型的数据

# 1 创建List

创建可变的List

// 创建一个空的可变List
val list1 = mutableListOf<String>()

// 创建了一个包含三个元素的List
var list2 = mutableListOf("Doubi", "Shabi" ,"Niubi")
1
2
3
4
5

创建不可变List

// 创建一个包含三个整数的不可变List
val immutableList = listOf(1, 2, 3)

// 创建一个不可变的空List
val emptyList = emptyList<String>()
1
2
3
4
5

不可变列表不允许添加、删除或修改其中的元素。

但是一个不能添加元素的空列表有什么用呢?

有时候可能希望在某个函数中返回一个空集合作为默认值,而不是 null。这样可以避免空指针异常,并使代码更加健壮。

# 2 基础操作

# 可变和不可变转换

不可变 List 和可变 List 可以相互转换。

fun main() {
    val list1 = listOf<String>()
    val list2 = list1.toMutableList()   // 将不可变list转换为可变list
    list2.add("Doubi")
}
1
2
3
4
5

可以使用 List 的 toMutableList() 方法将不可变 List 转换为可变的 List。注意,原 List 不变,得到的是一个新的可变的 List。

同样,可以将可变 List 转换为 不可变 List。

fun main() {
    val list1 = mutableListOf<String>()
		val list2 = list1.toList()   				// 将可变list转换为不可变list
}
1
2
3
4

# 添加元素

fun main() {
    // 创建一个空的可变List
    val list = mutableListOf<String>()
    list.add("Doubi")       // 添加元素
    list += "Niubi"         // 添加元素
    println(list)           // [Doubi, Niubi]
}
1
2
3
4
5
6
7

除了可以使用 add() 方法添加元素,还可以使用 += 运算符添加元素。这在C++中叫运算符重载。

也可以使用 addAll 方法将其他容器中的元素全部添加到 List 中:

val list = mutableListOf<String>()
list.addAll(mutableListOf("Caibi", "Mengbi"))
1
2

# 获取长度

可以使用 size 属性来获取 List 的长度:

// 创建一个空的可变List
val list = mutableListOf<String>("Doubi", "Shabi", "Niubi")
println(list.size)  // 通过size属性获取长度
1
2
3

# 访问元素

可以使用下标来访问列表中的元素,从0开始,如果可能越界,可以使用 getOrElsegetOrNull 获取。

fun main() {
    // 创建一个空的可变List
    val list = mutableListOf<String>("Doubi", "Shabi", "Niubi")

    val firstElement1 = list[0]   // 通过下标来访问第一个元素
    val firstElement2 = list.first()   // 访问第一个元素
    var lastElement1 = list.last()       // 获取最后一个元素
    var lastElement2 = list[list.lastIndex]       // 通过下标获取最后一个元素


    var element1 = list.get(1)
    var element5 = list.getOrElse(5) { "Doubi" }		// 如果越界,则返回lambda的值
    var element6 = list.getOrNull(6) ?: "Doubi"     // 如果越界,返回null值,可以结合?:操作符来进行空处理
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

和数字的操作方式基本是一样的。

# 插入元素

可以使用 add(index, element) 方法在指定位置插入元素:

val list = mutableListOf("Doubi", "Shabi", "Niubi")
list.add(1, "Caibi")  // 在索引 1 处插入元素
println(list)   // 输出:[Doubi, Caibi, Shabi, Niubi]
1
2
3

也可以使用 addAll 方法插入其他的列表或集合:

val list = mutableListOf("Doubi", "Shabi", "Niubi")

list.addAll(mutableListOf("Caibi", "Jianbi"))  // 在List末尾添加另一个list中的所有元素
list.addAll(1, mutableListOf("Caibi", "Jianbi"))  // 在索引 1 处插入另一个list中的所有元素
1
2
3
4

# 删除元素

fun main() {
    val list = mutableListOf("Red", "Green", "Blue", "Yellow", "Pink", "Blue", "Blue")
    list.removeFirst()   		    // 删除第一个元素
    list.removeLast()   		    // 删除最后一个元素
    list.removeAt(1) 						// 按照索引删除元素
    list.remove("Blue")     		// 删除列表中所有的Blue元素
    list -= "Yellow"            // 删除Yellow元素
    println(list)
}
1
2
3
4
5
6
7
8
9

除了可以使用 remove 相关的方法删除,还可以使用 -= 运算符进行删除。

我们还可以使用 lambda 表达式进行判断,然后删除指定的元素。

举个栗子:

fun main() {
    val list = mutableListOf("Red", "Green", "Blue", "Yellow", "Pink", "Blue", "Blue")
    list.removeIf { it == "Blue" }      // 根据条件删除,删除所有的 Blue
    println(list)
}
1
2
3
4
5

在上面使用 removeIf 函数,结合 lambda 表达式,根据指定的条件删除元素。it 表示 List 中的每一个元素。

# 清空List

val list = mutableListOf("Doubi", "Shabi", "Niubi")
list.clear()
1
2

# 3 遍历List

遍历 List 和遍历 数组的方式是一样的。

使用 for-in 循环

可以使用 for-in 循环来遍历数组的元素。

fun main() {
    val list = mutableListOf("Red", "Green", "Blue")

    for (element in list) {   // 依次取出数组中的每个元素
        println(element)
    }
}
1
2
3
4
5
6
7

使用索引和 indices 属性

如果需要访问数组的索引以及元素,可以使用数组的 indices 属性,然后通过索引来访问元素。

fun main() {
    val list = mutableListOf("Red", "Green", "Blue")

    for (index in list.indices) {     // index 每个元素的索引
        println("Index: $index, Element: ${list[index]}")     // 通过索引取出每个元素
    }
}
1
2
3
4
5
6
7

使用 forEach 函数

Kotlin 提供了 forEach 函数,可以结合 lambda 表达式进行遍历

fun main() {
    val list = mutableListOf("Red", "Green", "Blue")
    list.forEach {
        println(it)
    }
}
1
2
3
4
5
6

使用 forEachIndexed 函数

如果需要同时访问索引和元素,可以使用 forEachIndexed 函数。

fun main() {
    val list = mutableListOf("Red", "Green", "Blue")

    list.forEachIndexed { index, element ->
        println("Index: $index, Element: $element")
    }
}
1
2
3
4
5
6
7

使用迭代器

还可以通过获取数组的迭代器来遍历元素。

fun main() {
    val list = mutableListOf("Red", "Green", "Blue")

    val iterator = list.iterator()    // 获取数组的迭代器对象,通过迭代器对象取出数组中的元素
    while (iterator.hasNext()) {        // 判断是否有下一个元素
        val color = iterator.next()     // 取出下一个元素
        println(color)
    }
}
1
2
3
4
5
6
7
8
9

# 4 解构

什么是解构?

解构就是用集合 List 一次性给多个变量赋值。

举个栗子:

fun main() {
    val list = mutableListOf("Red", "Green", "Blue")
    val (one, two, three) = list
    println(three)      // Blue
}
1
2
3
4
5

在上面的代码中,通过 List 同时给 one 、two、three 三个变量赋值。

注意,被赋值的变量要比 List 的元素少,如果比 List 的元素多会报错。

如果想取其中的某几个元素,该如何操作呢,例如 List 有5个元素,想取其中的第1个和第4个元素给变量赋值。

fun main() {
    val list = mutableListOf("Red", "Green", "Blue", "Yellow" , "Gray")
    val (str1, _, _, str2) = list
    println(str1)      // Red
    println(str2)      // Yellow
}
1
2
3
4
5
6

在上面的代码中,分别用第1个和第4个元素给变量 str1 和 变量 str2 赋值,忽略的元素使用 _ 占位。

# 5 其他常用操作

# 判断 List 是否为空

在 Kotlin 中,可以使用 isEmpty() 方法判断 List 是否为空,使用 isNotEmpty() 方法判断 List 是否不为空。

val numbers = mutableListOf<Int>()
if (numbers.isEmpty()) {
    println("集合为空")
}

numbers.addAll(listOf(1, 2, 3, 4, 5))
if (numbers.isNotEmpty()) {
    println("集合不为空")
}
1
2
3
4
5
6
7
8
9

# 检查是否包含某个元素

在 Kotlin 中,你可以使用 contains() 方法来检查是否包含某个元素。

val numbers = listOf(1, 2, 3)
println(numbers.contains(2)) // true
1
2

# 将 List 中的元素拼接成字符串

在 Kotlin 中,可以使用 joinToString() 函数将 List 中的元素使用指定的连接符拼接成一个字符串。

val numbers = listOf(1, 2, 3)
val numberString = numbers.joinToString(", ")
println(numberString) // 输出:1, 2, 3
1
2
3

# 将字符串分割成 List

在 Kotlin 中,可以使用 split() 函数将一个字符串使用指定的分隔符分割成一个 List。

val str = "1,2,3,4,5"
val list = str.split(",")
println(list) // 输出:[1, 2, 3, 4, 5]
1
2
3

# 使用 List 中的元素生成新的 List

在 Kotlin 中,你可以使用 map() 函数来处理 List 中的元素,然后生成一个新的 List。

val numbers = listOf(1, 2, 3)
val doubledNumbers = numbers.map { it * 2 }
println(doubledNumbers) // [2, 4, 6]
1
2
3

# 过滤 List 中的元素

在 Kotlin 中,可以使用 filter() 函数来过滤 List 中的元素,生成一个新的 List。

val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers) // [2, 4]
1
2
3

在上面,通过 filter() 函数,过滤满足 it % 2 == 0 的元素,形成一个新的 List。

# 判断 List 中的元素是否有元素满足指定条件

在 Kotlin 中,可以使用 any() 函数来判断 List 中是否有元素满足指定的条件。

val numbers = listOf(1, 2, 3, 4, 5)
val f = numbers.any { it % 2 == 0 }
println(f) // true
1
2
3

# 判断 List 中的元素是否都满足指定条件

在 Kotlin 中,可以使用 all() 函数来判断 List 中所有的元素是否都满足指定的条件。

val numbers = listOf(1, 2, 3, 4, 5)
val f = numbers.all { it % 2 == 0 }
println(f) // false
1
2
3

# 7.3 Set

在 Kotlin 中,Set 与 List 有一些不同之处:Set 是一种容器类型,不允许包含重复元素,而且 Set 是无序的。Set 主要用于集合运算,例如求交集、并集、差集等。

Set 的主要特点包括:

  • 不允许重复元素
  • 无序,不能保证元素的顺序
  • 可以被修改
  • 可以存储不同的数据类型

# 1 创建 Set

创建可变的 Set 的方式,可以向其中添加或删除元素。

// 创建一个空的 Set
val emptySet = mutableSetOf<String>()

// 创建一个包含元素的 Set
val colorSet = mutableSetOf("Red", "Green", "Blue")

// 使用其他 List 或 Set 来创建
val fruits = setOf("apple", "banana", "orange").toMutableSet()
1
2
3
4
5
6
7
8

创建不可变的 Set 的方式,其中的元素不可修改。

// 创建一个空的 Set
val emptySet = setOf<String>()

// 创建一个包含元素的 Set
val fruits = setOf("apple", "banana", "orange")
1
2
3
4
5

# 2 基础操作

# 可变与不可变转换

不可变 Set 和可变 Set 可以相互转换。

将不可变 Set 转换为可变 Set:

fun main() {
    val colorSet = setOf("red", "green", "blue")
    val newColorSet = colorSet.toMutableSet()
    val newColorList = colorSet.toMutableList()
}
1
2
3
4
5

可以使用 Set 的 toMutableSet() 方法将不可变 Set 转换为可变的 Set。注意,原 Set 不变,得到的是一个新的可变的 Set。

甚至还可以使用 toMutableList() 方法直接转换为可变的 List。

同样,可以将可变 Set 转换为 不可变 Set 或 不可变 List。

fun main() {
    // 创建一个包含元素的 Set
    val colorSet = mutableSetOf("Red", "Green", "Blue")
    val newColorSet = colorSet.toSet()      // 得到一个新的不可变的Set
    val newColorList = colorSet.toList()    // 得到一个新的不可变的List
}
1
2
3
4
5
6

所以 List 和 Set 可以相互转换,还可以相互在可变和不可变之间进行转换。

所以如果我们想去掉 List 中的重复元素可以进行如下操作:

fun main() {
    val list1 = listOf<String>("red", "red", "green", "blue")
    val list2 = list1.toSet().toList()		// 先变Set再转回List
    println(list2)      // [red, green, blue]
}
1
2
3
4
5

可以先将有重复元素的 List 转换为 Set,然后再转换为 List。 但其实 List 有提供去重的方法,如下:

fun main() {
    val list1 = listOf<String>("red", "red", "green", "blue")
    val list2 = list1.distinct()		// 去重
    println(list2)      // [red, green, blue]
}
1
2
3
4
5

可以使用 distinct() 方法去掉重复的元素。

# 添加元素

可以使用 add()+=addAll() 向 Set 中添加元素:

fun main() {
    val numbers = mutableSetOf<Int>()
    numbers.add(1)      // 可以使用add方法添加元素
    numbers += 2        // 可以使用运算符来添加元素
    numbers.addAll(listOf(3, 4, 5))      // 可以使用addAll添加其他容器
}
1
2
3
4
5
6

# 访问元素

在 Java或其他很多语言中因为 Set 是无序的,所以无法通过下标来访问 Set 中的元素。如果要访问 Set 中的元素,只能通过迭代方式来获取。

但是 Kotlin 比较牛逼,可以使用下标来访问,真是小刀拉屁股——开眼了。

举个栗子:

fun main() {
    // 创建一个包含元素的 Set
    val colors = setOf("red", "green", "blue")
    val element1 = colors.elementAt(0)
    val element5 = colors.elementAtOrElse(5) { "black" }   // 获取index为8的元素,获取不到就使用lambda返回black
    val element6 = colors.elementAtOrNull(6) ?: "black"    // 获取index为9的元素,获取不到就返回null,结合?:操作符返回black
}
1
2
3
4
5
6
7

和 List 提供的方法是一样的。

那么问题来了,它是如何实现的,什么原理呢?

其实底层是使用迭代器来遍历 Set,依次取出 Set 中的元素,然后使用取出的次数当索引来返回指定的元素。

突如起来的骚气,差点闪到我的老妖。所以这个索引意义不大,和添加的顺序很可能不一致。

当然我们可以创建 LinkedHashSet ,这个是有序的 Set,那么索引就意义了。

举个栗子:

fun main() {
    // 创建可变LinkedHashSet
    val linkedHashSet1 = LinkedHashSet<String>()
    linkedHashSet1.add("red")
    linkedHashSet1.add("red")
    linkedHashSet1.add("green")
    println(linkedHashSet1)         // [red, green]

    // 创建不可变LinkedHashSet
    val linkedHashSet2 = linkedSetOf("red", "red", "green")
    println(linkedHashSet2)         // [red, green]
}
1
2
3
4
5
6
7
8
9
10
11
12

如果想创建有序的 Set,可以使用上面的方式来创建可变或不可变的有序 Set。

# 获取长度

你可以使用 size 属性来获取 Set 的长度:

val numbers = setOf(1, 2, 3)
println(numbers.size) // 3
1
2

# 删除元素

举个栗子:

fun main() {
    // 创建一个包含元素的 Set
    val colorSet = mutableSetOf("red", "green", "blue", "yellow", "gray", "black")
    colorSet.remove("red")
    colorSet -= "green"			// 使用 -= 删除
    colorSet.removeIf() { it == "blue" }				// 删除元素是blue的元素
    colorSet.removeAll(listOf("yellow", "gray"))		// 使用容器批量删除
    println(colorSet) // [black]
}
1
2
3
4
5
6
7
8
9

可以使用 remove-= 来删除,也可以使用 removeIf 通过 lambda 表达式来过滤进行删除。

还可以根据容器批量删除 Set 中的元素。

# 清空 Set

你可以使用 clear() 方法来清空 Set:

val numbers = mutableSetOf(1, 2, 3)
numbers.clear()
println(numbers) // []
1
2
3

# 将 Set 转换为 List

使用 toList() 方法可以将 Set 转换为 List:

val numbers = setOf(1, 2, 3)
val numList = numbers.toList()
println(numList) // [1, 2, 3]
1
2
3

# 3 遍历 Set

在 Kotlin 中,遍历 Set 可以使用多种方式,包括 for 循环、forEach() 方法、for-in 循环以及迭代器。以下是这些方法的示例:

使用 for-in 循环

val fruits = setOf("apple", "banana", "orange")

for (fruit in fruits) {
    println(fruit)
}
1
2
3
4
5

使用 forEach() 方法

val fruits = setOf("apple", "banana", "orange")

fruits.forEach { fruit ->
    println(fruit)
}
1
2
3
4
5

使用迭代器

val fruits = setOf("apple", "banana", "orange")
val iterator = fruits.iterator()

while (iterator.hasNext()) {
    val fruit = iterator.next()
    println(fruit)
}
1
2
3
4
5
6
7

使用 for 循环

因为 Set 没有索引,所以可以使用 Set.toList() 方法将 Set 转换为 List,并使用 List.size 属性确定 List 的长度,然后使用 for 循环遍历 List 中的元素。

val fruits = setOf("apple", "banana", "orange")
val fruitsList = fruits.toList()

for (i in 0 until fruitsList.size) {
    println(fruitsList[i])
}
1
2
3
4
5
6

# 4 其他常用操作

# 判断 Set 是否为空

可以使用 isEmpty() 方法判断 Set 是否为空,使用 isNotEmpty() 方法判断 Set 是否不为空:

val numbers = setOf<Int>()
if (numbers.isEmpty()) {
    println("Set为空")
}

val numbers2 = setOf(1, 2, 3, 4, 5)
if (numbers2.isNotEmpty()) {
    println("Set不为空")
}
1
2
3
4
5
6
7
8
9

# 检查是否包含某个元素

你可以使用 contains() 方法来检查 Set 是否包含某个元素:

val numbers = setOf(1, 2, 3)
println(numbers.contains(2)) // true
1
2

# 将 Set 中的元素拼接为字符串

可以使用 joinToString() 函数将 Set 中的元素使用指定的连接符拼接为一个字符串:

val numbers = setOf(1, 2, 3)
val numberString = numbers.joinToString(", ")
println(numberString) // "1, 2, 3"
1
2
3

# 使用 Set 中的元素生成新的 Set

与 List 一样,在 Kotlin 中,你可以使用 map() 方法处理 Set 中的元素,然后生成一个新的 Set:

val numbers = setOf(1, 2, 3)
val squaredNumbers = numbers.map { it * 2 }.toSet()
println(squaredNumbers) // [2, 4, 6]
1
2
3

Set 也提供了 filter()any()every() 方法,可以用来过滤、检查是否有元素满足条件以及是否所有元素都满足条件。

# 并集、交集和差集

在 Kotlin 中,Set 提供了 union()intersect()subtract() 方法,可以用来执行并集、交集和差集操作。

并集示例:

val set1 = setOf(1, 2, 3)
val set2 = setOf(3, 4, 5)
val unionSet = set1.union(set2)
println(unionSet) // [1, 2, 3, 4, 5]
1
2
3
4

交集示例:

val set1 = setOf(1, 2, 3)
val set2 = setOf(3, 4, 5)
val intersectionSet = set1.intersect(set2)
println(intersectionSet) // [3]
1
2
3
4

差集示例:

val set1 = setOf(1, 2, 3)
val set2 = setOf(3, 4, 5)
val differenceSet = set1.subtract(set2)
println(differenceSet) // [1, 2]
1
2
3
4

这些操作在 Kotlin 的 Set 中非常有用,可以方便地执行集合运算。

# 7.4 Map

Map是一种键值对的数据结构,其中每个键对应一个值。

例如有一份数据:

姓名 成绩
zhangsan 94
lisi 96
wangwu 91

在Kotlin中,可以使用Map来存储这种键值对数据,将姓名作为键,成绩作为值:

val studentGrades = mapOf(
    "zhangsan" to 94,
    "lisi" to 96,
    "wangwu" to 91
)
1
2
3
4
5

通过 to 关键字来设置 key 和 value。

这样可以很容易通过姓名键获取对应的成绩值。

# 1 创建Map

创建可变Map

// 创建一个包含元素的可变Map
val studentGrades = mutableMapOf(
    "zhangsan" to 94,
    "lisi" to 96,
    "wangwu" to 91
)

// 创建一个空可变Map
val emptyMap = mutableMapOf<String, Int>()
1
2
3
4
5
6
7
8
9

这种方式创建的Map是可变的(mutable),你可以随时添加、修改或删除键值对。

没有指定类型的时候,Kotlin会自动推断键和值的数据类型。

创建不可变Map

// 创建一个包含元素的不可变Map
val studentGrades = mapOf(
    "zhangsan" to 94,
    "lisi" to 96,
    "wangwu" to 91
)

// 创建一个空不可变Map
val emptyMap = emptyMap<String, Int>()
1
2
3
4
5
6
7
8
9

这种方式创建的Map是不可变的(immutable),意味着一旦初始化后,不能再修改其中的键值对。

其实 to 是一个函数,创建的是一个 Pair() 对象。

所以我们也可以直接创建 Pair 对象,举个栗子:

val studentGrades = mapOf(
    Pair("zhangsan", 94),
    Pair("lisi", 96),
    Pair("wangwu", 91)
)
1
2
3
4
5

当然了,我们一般不会这么写。

# 2 基础操作

# 添加元素

fun main() {
    val numbers = mutableMapOf<String, Int>()
    numbers["one"] = 1					// 添加或修改,会覆盖或修改之前的值
    numbers.put("one", 2)       // 添加或修改,会覆盖或修改之前的值
    numbers += "two" to 2       // 添加或修改,会覆盖或修改之前的值

    val value2 = numbers.putIfAbsent("two", 3)   // 如果key存在就不添加,并返回存在的value,如果key不存在就添加,返回null
    println(value2) // 2
    val value3 = numbers.getOrPut("three") { 4 } // 获取指定的key元素,若不存在则将该元素加入到map中,并返回value,如果存在就不添加,并返回value
    println(value3)
    println(numbers) // {one=2, two=2, three=4}
}
1
2
3
4
5
6
7
8
9
10
11
12

putIfAbsent 和 getOrPut 的区别:

  • putIfAbsent:如果key存在就不添加,并返回存在的value,如果key不存在就添加,返回null;
  • getOrPut: 获取指定的key元素,如果存在就不添加,并返回value,如果key不存在就添加,也返回value。

# 访问元素

访问元素时,通过键来访问:

fun main() {
    val numbers = mapOf(
        "one" to 1,
        "two" to 2,
        "three" to 3
    )
  
    println(numbers["one"])         // 如果key不存在,返回null
    println(numbers.get("two"))     // 如果key不存在,返回null
    println(numbers.getValue("two"))     // 如果key不存在,报错
    println(numbers.getOrDefault("two", "unknow"))     // 如果key不存在,返回defalutValue
    println(numbers.getOrElse("two") { "unknow" })   // 如果key不存在,返回lambda表达式结果
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 获取长度

获取Map的长度使用size属性:

val numbers = mapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)
println(numbers.size) // 3
1
2
3
4
5
6

# 删除元素

删除元素通过键来进行操作:

val numbers = mutableMapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)

numbers.remove("one")      // 删除
numbers -= "two"           // 删除
println(numbers) // {three=3}
1
2
3
4
5
6
7
8
9

# 清空Map

清空Map使用clear()方法:

val numbers = mutableMapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)
numbers.clear()
println(numbers) // {}
1
2
3
4
5
6
7

# 3 遍历操作

在Kotlin中,遍历Map可以使用for-in循环、forEach函数、keysvalues属性:

使用for-in循环

val studentGrades = mapOf(
    "Alice" to 90,
    "Bob" to 80,
    "Charlie" to 85
)

for ((key, value) in studentGrades) {
    println("$key - $value")
}
1
2
3
4
5
6
7
8
9

使用forEach函数

val studentGrades = mapOf(
    "Alice" to 90,
    "Bob" to 80,
    "Charlie" to 85
)

// 方式一,使用it
studentGrades.forEach {
    println("${it.key} - ${it.value}")
}

// 方式二,使用key、value
studentGrades.forEach { (key, value) ->
    println("$key - $value")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

使用keysvalues属性

val studentGrades = mapOf(
    "Alice" to 90,
    "Bob" to 80,
    "Charlie" to 85
)

for (key in studentGrades.keys) {
    val value = studentGrades[key]
    println("$key - $value")
}

for (value in studentGrades.values) {
    println("$value")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4 其他常用操作

# 判断Map是否为空

可以通过isEmpty()方法判断Map是否为空,通过isNotEmpty()方法判断Map是否不为空:

val numbers = emptyMap<String, Int>()
if (numbers.isEmpty()) {
    println("Map为空")
}

val studentGrades = mapOf(
    "Alice" to 90,
    "Bob" to 80,
    "Charlie" to 85
)
if (studentGrades.isNotEmpty()) {
    println("Map不为空")
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 检查包含某个键

val numbers = mapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)
println(numbers.containsKey("two")) // true
1
2
3
4
5
6

# 检查包含某个值

val numbers = mapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)
println(numbers.containsValue(2)) // true
1
2
3
4
5
6

# 获取Map中的所有键

val numbers = mapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)
val keys = numbers.keys.toList()
println(keys) // [one, two, three]
1
2
3
4
5
6
7

# 将Map中的键值对拼接为字符串

val numbers = mapOf(
    "one" to 1,
    "two" to 2,
    "three" to 3
)
val numberString = numbers.entries.joinToString { (key, value) -> "$key: $value" }
println(numberString) // "one: 1, two: 2, three: 3"
1
2
3
4
5
6
7