3.2.3 集合

原文链接: https://kotlinlang.org/docs/kotlin-tour-collections.html

3.2.3 集合

编程时,能够把数据分组到某种结构中以便后续处理是很有用的。Kotlin 提供的集合正是为此而生。

Kotlin 提供了以下用于把项目分组的集合:

| 集合类型 | 说明 | | Lists | 有序的项目集合 | | Sets | 只包含唯一项目的无序集合 | | Maps | 键值对集合,其中键是唯一的,并且只映射到一个值 |

每种集合类型都可以是可变的或只读的。

List

列表按项目添加的顺序存储它们,并允许出现重复的项目。

要创建只读列表(List),请使用 listOf() 函数。

要创建可变列表(MutableList),请使用 mutableListOf() 函数。

创建列表时,Kotlin 可以推断所存储项目的类型。要显式声明类型,请在列表声明之后、尖括号 <> 内写上该类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fun main() {
    // Read only list
    val readOnlyShapes = listOf("triangle", "square", "circle")
    println(readOnlyShapes)
    // [triangle, square, circle]

    // Mutable list with explicit type declaration
    val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
    println(shapes)
    // [triangle, square, circle]
}

提示: 为防止不必要的修改,你可以把可变列表赋值给一个 List,从而创建它的只读视图:kotlin val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle") val shapesLocked: List<String> = shapes 这也称为类型转换。

列表是有序的,因此要访问列表中的某个项目,请使用索引访问运算符 []:

1
2
3
4
5
fun main() {
    val readOnlyShapes = listOf("triangle", "square", "circle")
    println("The first item in the list is: ${readOnlyShapes[0]}")
    // The first item in the list is: triangle
}

要获取列表中的第一个或最后一个项目,请分别使用 .first() 和 .last() 函数:

1
2
3
4
5
fun main() {
    val readOnlyShapes = listOf("triangle", "square", "circle")
    println("The first item in the list is: ${readOnlyShapes.first()}")
    // The first item in the list is: triangle
}

注意: .first() 和 .last() 函数是扩展函数的例子。要在对象上调用扩展函数,请把函数名写在对象之后,并用句点 . 连接。扩展函数在进阶导览中有详细讲解。现在你只需要知道如何调用它们。

要获取列表中项目的数量,请使用 .count() 函数:

1
2
3
4
5
fun main() {
    val readOnlyShapes = listOf("triangle", "square", "circle")
    println("This list has ${readOnlyShapes.count()} items")
    // This list has 3 items
}

要检查某个项目是否在列表中,请使用 in 运算符:

1
2
3
4
5
fun main() {
    val readOnlyShapes = listOf("triangle", "square", "circle")
    println("circle" in readOnlyShapes)
    // true
}

要在可变列表中添加或移除项目,请分别使用 .add() 和 .remove() 函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fun main() {
    val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
    // Add "pentagon" to the list
    shapes.add("pentagon")
    println(shapes)
    // [triangle, square, circle, pentagon]

    // Remove the first "pentagon" from the list
    shapes.remove("pentagon")
    println(shapes)
    // [triangle, square, circle]
}

Set

列表是有序的并允许重复项目,而集(set)是无序的,并且只存储唯一项目。

要创建只读集(Set),请使用 setOf() 函数。

要创建可变集(MutableSet),请使用 mutableSetOf() 函数。

创建集时,Kotlin 可以推断所存储项目的类型。要显式声明类型,请在集声明之后、尖括号 <> 内写上该类型:

1
2
3
4
5
6
7
8
9
fun main() {
    // Read-only set
    val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
    // Mutable set with explicit type declaration
    val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")

    println(readOnlyFruit)
    // [apple, banana, cherry]
}

从上面的示例可以看到,由于集只包含唯一元素,重复的 "cherry" 项目被丢弃了。

提示: 为防止不必要的修改,你可以把可变集赋值给一个 Set,从而创建它的只读视图:kotlin val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry") val fruitLocked: Set<String> = fruit

注意: 由于集是无序的,你无法访问特定索引处的项目。

要获取集中项目的数量,请使用 .count() 函数:

1
2
3
4
5
fun main() {
    val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
    println("This set has ${readOnlyFruit.count()} items")
    // This set has 3 items
}

要检查某个项目是否在集中,请使用 in 运算符:

1
2
3
4
5
fun main() {
    val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
    println("banana" in readOnlyFruit)
    // true
}

要在可变集中添加或移除项目,请分别使用 .add() 和 .remove() 函数:

1
2
3
4
5
6
7
8
fun main() {
    val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")
    fruit.add("dragonfruit")    // Add "dragonfruit" to the set
    println(fruit)              // [apple, banana, cherry, dragonfruit]

    fruit.remove("dragonfruit") // Remove "dragonfruit" from the set
    println(fruit)              // [apple, banana, cherry]
}

Map

映射(map)以键值对的形式存储项目。你通过引用键来访问值。可以把映射想象成一份菜单:只要找到你想吃的食物(键),就能查到它的价格(值)。当你想在不使用列表那种数字索引的情况下查找某个值时,映射非常有用。

注意: * 映射中的每个键都必须是唯一的,这样 Kotlin 才能理解你想获取哪个值。 * 映射中可以出现重复的值。

要创建只读映射(Map),请使用 mapOf() 函数。

要创建可变映射(MutableMap),请使用 mutableMapOf() 函数。

创建映射时,Kotlin 可以推断所存储项目的类型。要显式声明类型,请在映射声明之后、尖括号 <> 内写上键和值的类型。例如:MutableMap<String, Int>。键的类型是 String,值的类型是 Int。

创建映射最简单的方式是在每个键与其对应的值之间使用 to:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fun main() {
    // Read-only map
    val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    println(readOnlyJuiceMenu)
    // {apple=100, kiwi=190, orange=100}

    // Mutable map with explicit type declaration
    val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    println(juiceMenu)
    // {apple=100, kiwi=190, orange=100}
}

提示: 为防止不必要的修改,你可以把可变映射赋值给一个 Map,从而创建它的只读视图:kotlin val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100) val juiceMenuLocked: Map<String, Int> = juiceMenu

要访问映射中的某个值,请用它的键配合索引访问运算符 []:

1
2
3
4
5
6
fun main() {
    // Read-only map
    val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    println("The value of apple juice is: ${readOnlyJuiceMenu["apple"]}")
    // The value of apple juice is: 100
}

注意: 如果你用一个映射中不存在的键去访问键值对,会看到 null 值:kotlin fun main() { //sampleStart // Read-only map val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100) println("The value of pineapple juice is: ${readOnlyJuiceMenu["pineapple"]}") // The value of pineapple juice is: null //sampleEnd } {kotlin-runnable=“true” kotlin-min-compiler-version=“1.3” id=“kotlin-tour-map-no-key” validate=“false”} 本次导览会在后面的空安全一章中讲解 null 值。

你也可以用索引访问运算符 [] 向可变映射中添加项目:

1
2
3
4
5
6
fun main() {
    val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    juiceMenu["coconut"] = 150 // Add key "coconut" with value 150 to the map
    println(juiceMenu)
    // {apple=100, kiwi=190, orange=100, coconut=150}
}

要从可变映射中移除项目,请使用 .remove() 函数:

1
2
3
4
5
6
fun main() {
    val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    juiceMenu.remove("orange")    // Remove key "orange" from the map
    println(juiceMenu)
    // {apple=100, kiwi=190}
}

要获取映射中项目的数量,请使用 .count() 函数:

1
2
3
4
5
6
fun main() {
    // Read-only map
    val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    println("This map has ${readOnlyJuiceMenu.count()} key-value pairs")
    // This map has 3 key-value pairs
}

要检查某个键是否已经包含在映射中,请使用 .containsKey() 函数:

1
2
3
4
5
fun main() {
    val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    println(readOnlyJuiceMenu.containsKey("kiwi"))
    // true
}

要获取映射的键集合或值集合,请分别使用 keys 和 values 属性:

1
2
3
4
5
6
7
fun main() {
    val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    println(readOnlyJuiceMenu.keys)
    // [apple, kiwi, orange]
    println(readOnlyJuiceMenu.values)
    // [100, 190, 100]
}

注意: keys 和 values 是对象属性的例子。要访问对象的属性,请把属性名写在对象之后,并用句点 . 连接。属性会在类一章中详细讨论。在导览的这一阶段,你只需要知道如何访问它们。

要检查某个键或值是否在映射中,请使用 in 运算符:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fun main() {
    val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
    println("orange" in readOnlyJuiceMenu.keys)
    // true

    // Alternatively, you don't need to use the keys property
    println("orange" in readOnlyJuiceMenu)
    // true

    println(200 in readOnlyJuiceMenu.values)
    // false
}

关于可以对集合做些什么的更多信息,请参阅集合。

现在你已经了解了基本类型以及如何管理集合,接下来该探索可以在程序中使用的控制流了。

练习

习题 1

你有一个「绿色」数字列表和一个「红色」数字列表。请补全代码,打印出总共有多少个数字。

1
2
3
4
5
fun main() {
    val greenNumbers = listOf(1, 4, 23)
    val redNumbers = listOf(17, 2)
    // Write your code here
}
1
2
3
4
5
6
fun main() {
    val greenNumbers = listOf(1, 4, 23)
    val redNumbers = listOf(17, 2)
    val totalCount = greenNumbers.count() + redNumbers.count()
    println(totalCount)
}

示例解答

习题 2

你有一个服务器支持的协议集。用户请求使用某个特定的协议。请补全程序,检查所请求的协议是否受支持(isSupported 必须是布尔值)。

1
2
3
4
5
6
fun main() {
    val SUPPORTED = setOf("HTTP", "HTTPS", "FTP")
    val requested = "smtp"
    val isSupported = // Write your code here
    println("Support for $requested: $isSupported")
}

提示

请确保以大写形式检查所请求的协议。你可以借助 .uppercase() 函数来完成。

1
2
3
4
5
6
fun main() {
    val SUPPORTED = setOf("HTTP", "HTTPS", "FTP")
    val requested = "smtp"
    val isSupported = requested.uppercase() in SUPPORTED
    println("Support for $requested: $isSupported")
}

示例解答

习题 3

定义一个映射,把 1 到 3 的整数与它们对应的英文拼写关联起来。用这个映射拼出给定的数字。

1
2
3
4
5
fun main() {
    val number2word = // Write your code here
    val n = 2
    println("$n is spelled as '${<Write your code here >}'")
}
1
2
3
4
5
fun main() {
    val number2word = mapOf(1 to "one", 2 to "two", 3 to "three")
    val n = 2
    println("$n is spelled as '${number2word[n]}'")
}

示例解答

另请参阅

上一步

下一步