1、集合类型
Kotlin 标准库提供了基本集合类型的实现: set、list 以及 map。 一对接口代表每种集合类型:
1、一个 只读 接口,提供访问集合元素的操作。
2、一个 可变 接口,通过写操作扩展相应的只读接口:添加、删除和更新其元素。
请注意,更改可变集合不需要它是以 var
定义的变量:写操作修改同一个可变集合对象,因此引用不会改变。 但是,如果尝试对 val
集合重新赋值,你将收到编译错误。
val numbers = mutableListOf("one", "two", "three", "four")
numbers.add("five") // 这是可以的
//numbers = mutableListOf("six", "seven") // 编译错误
2、List
List
以指定的顺序存储元素,并提供使用索引访问元素的方法。索引从 0 开始 – 第一个元素的索引 – 直到 最后一个元素的索引
即 (list.size - 1)
。
val numbers = listOf("one", "two", "three", "four")
println("Number of elements: ${numbers.size}")
println("Third element: ${numbers.get(2)}")
println("Fourth element: ${numbers[3]}")
println("Index of element \"two\" ${numbers.indexOf("two")}")
MutableList
是可以进行写操作的 List
,例如用于在特定位置添加或删除元素。
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.removeAt(1)
numbers[0] = 0
numbers.shuffle()
println(numbers)
3、Map
Map
不是 Collection
接口的继承者;但是它也是 Kotlin 的一种集合类型。 Map
存储 键-值 对(或 条目);键是唯一的,但是不同的键可以与相同的值配对。Map
接口提供特定的函数进行通过键访问值、搜索键和值等操作。
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
println("All keys: ${numbersMap.keys}")
println("All values: ${numbersMap.values}")
if ("key2" in numbersMap) println("Value by key \"key2\": ${numbersMap["key2"]}")
if (1 in numbersMap.values) println("The value 1 is in the map")
if (numbersMap.containsValue(1)) println("The value 1 is in the map") // 同上
MutableMap
是一个具有写操作的 Map
接口,可以使用该接口添加一个新的键值对或更新给定键的值。
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
numbersMap["one"] = 11
println(numbersMap)
4、集合的类型
创建集合的最常用方法是使用标准库函数
listOf
、
setOf
、
mutableListOf
、
mutableSetOf
。
Map 也有这样的函数
mapOf()
与
mutableMapOf()
5、空集合
还有用于创建没有任何元素的集合的函数:emptyList()
、emptySet()
与 emptyMap()
。 创建空集合时,应指定集合将包含的元素类型。
val empty = emptyList()
6、具体类型构造函数
要创建具体类型的集合,例如 ArrayList 或 LinkedList,可以使用这些类型的构造函数。 类似的构造函数对于 Set 与 Map 的各实现中均有提供。
val linkedList = LinkedList(listOf("one", "two", "three"))
val presizedSet = HashSet(32)
7、复制
要创建与现有集合具有相同元素的集合,可以使用复制操作。标准库中的集合复制操作创建了具有相同元素引用的 浅 复制集合。 因此,对集合元素所做的更改会反映在其所有副本中。
在特定时刻通过集合复制函数,例如toList()
、toMutableList()
、toSet()
等等。创建了集合的快照。 结果是创建了一个具有相同元素的新集合 如果在源集合中添加或删除元素,则不会影响副本。副本也可以独立于源集合进行更改。
//原数组
val sourceList = mutableListOf(1, 2, 3)
//复制成可变数组
val copyList = sourceList.toMutableList()
//复制成只读数组
val readOnlyCopyList = sourceList.toList()
sourceList.add(4)
println("Copy size: ${copyList.size}")
//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)
8、迭代器
//迭代器 1
val numbers = listOf("one", "two", "three", "four")
val numbersIterator = numbers.iterator()
while (numbersIterator.hasNext()) {
println(numbersIterator.next())
}
遍历 Iterable 集合的另一种方法是众所周知的 for 循环。在集合中使用 for 循环时,将隐式获取迭代器。因此,以下代码与上面的示例等效:
//迭代器 2
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)
}
9、可变迭代器
为了迭代可变集合,于是有了 MutableIterator
来扩展 Iterator
使其具有元素删除函数 remove()
。因此,可以在迭代时从集合中删除元素。
val numbers = mutableListOf("one", "two", "three", "four")
val mutableIterator = numbers.iterator()
mutableIterator.next()
mutableIterator.remove()
println("After removal: $numbers")
10、区间与数列
Kotlin 可通过调用 kotlin.ranges
包中的 rangeTo()
函数及其操作符形式的 ..
轻松地创建两个值的区间。 通常,rangeTo()
会辅以 in
或 !in
函数。
if (i in 1..4) { // 等同于 1 <= i && i <= 4
print(i)
}
整数类型区间(IntRange
、LongRange
、CharRange
)还有一个拓展特性:可以对其进行迭代。 这些区间也是相应整数类型的等差数列。 这种区间通常用于 for
循环中的迭代。
for (i in 1..4) print(i)
//要反向迭代数字,请使用 downTo函数而不是 `..` 。
for (i in 4 downTo 1) print(i)
//也可以通过任意步长(不一定为 1 )迭代数字。 这是通过 step 函数完成的。
for (i in 1..8 step 2) print(i)
for (i in 8 downTo 1 step 2) print(i)
要迭代不包含其结束元素的数字区间,请使用 until
函数:
for (i in 1 until 10) { // i in [1, 10), 10被排除
print(i)
}
11、 映射
映射 转换从另一个集合的元素上的函数结果创建一个集合。 基本的映射函数是 map()
。 它将给定的 lambda 函数应用于每个后续元素,并返回 lambda 结果列表。 结果的顺序与元素的原始顺序相同。 如需应用还要用到元素索引作为参数的转换,请使用 mapIndexed()
。
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })
println(numbers.mapIndexed { idx, value -> value * idx })
如果转换在某些元素上产生 null
值,则可以通过调用 mapNotNull()
函数取代 map()
或 mapIndexedNotNull()
取代 mapIndexed()
来从结果集中过滤掉 null
值。
val numbers = setOf(1, 2, 3)
println(numbers.mapNotNull { if ( it == 2) null else it * 3 })
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })
12、字符串表示
如果需要以可读格式检索集合内容,请使用将集合转换为字符串的函数:joinToString()
与 joinTo()
。
joinToString()
根据提供的参数从集合元素构建单个 String
。 joinTo()
执行相同的操作,但将结果附加到给定的 Appendable
对象。
当使用默认参数调用时,函数返回的结果类似于在集合上调用 toString()
:各元素的字符串表示形式以空格分隔而成的 String
。
val numbers = listOf("one", "two", "three", "four")
println(numbers)
println(numbers.joinToString())
val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)
13、过滤
基本的过滤函数是 filter()
。当使用一个谓词来调用时,filter()
返回与其匹配的集合元素。对于 List
和Set
,过滤结果都是一个 List
,对 Map
来说结果还是一个 Map
。
val numbers = listOf("one", "two", "three", "four")
val longerThan3 = numbers.filter { it.length > 3 }
println(longerThan3)
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10}
println(filteredMap)
filter()
中的谓词只能检查元素的值。如果想在过滤中使用元素在集合中的位置,应该使用 filterIndexed()
。它接受一个带有两个参数的谓词:元素的索引和元素的值。
如果想使用否定条件来过滤集合,请使用 filterNot()
。它返回一个让谓词产生 false
的元素列表。
val numbers = listOf("one", "two", "three", "four")
val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5) }
val filteredNot = numbers.filterNot { it.length <= 3 }
println(filteredIdx)
println(filteredNot)
14、plus 与 minus 操作符
在 Kotlin 中,为集合定义了 plus
(+
) 和 minus
(-
) 操作符。 它们把一个集合作为第一个操作数;第二个操作数可以是一个元素或者是另一个集合。 返回值是一个新的只读集合:
-
plus
的结果包含原始集合 和 第二个操作数中的元素。 -
minus
的结果包含原始集合中的元素,但第二个操作数中的元素 除外。 如果第二个操作数是一个元素,那么minus
移除其在原始集合中的 第一次 出现;如果是一个集合,那么移除其元素在原始集合中的 所有 出现。
val numbers = listOf("one", "two", "three", "four")
val plusList = numbers + "five"
val minusList = numbers - listOf("three", "four")
println(plusList)
println(minusList)
15、Slice
slice()
返回具有给定索引的集合元素列表。 索引既可以是作为区间传入的也可以是作为整数值的集合传入的。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.slice(1..3))
println(numbers.slice(0..4 step 2))
println(numbers.slice(setOf(3, 5, 0)))
16、Take 与 drop
要从头开始获取指定数量的元素,请使用 take()
函数。 要从尾开始获取指定数量的元素,请使用 takeLast()
。 当调用的数字大于集合的大小时,两个函数都将返回整个集合。
要从头或从尾去除给定数量的元素,请调用 drop()
或 dropLast()
函数。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.take(3))
println(numbers.takeLast(3))
println(numbers.drop(1))
println(numbers.dropLast(5))
17、取单个元素-按位置取
为了检索特定位置的元素,有一个函数 elementAt()
。 用一个整数作为参数来调用它,你会得到给定位置的集合元素。 第一个元素的位置是 0
,最后一个元素的位置是 (size - 1)
。
elementAt()
对于不提供索引访问或非静态已知提供索引访问的集合很有用。 在使用 List
的情况下,使用索引访问操作符 (get()
或 []
)更为习惯
val numbers = linkedSetOf("one", "two", "three", "four", "five")
println(numbers.elementAt(3))
val numbersSortedSet = sortedSetOf("one", "two", "three", "four")
println(numbersSortedSet.elementAt(0)) // 元素以升序存储
//还有一些有用的别名来检索集合的第一个和最后一个元素:first() 和 last()
val numbers = listOf("one", "two", "three", "four", "five")
println(numbers.first())
println(numbers.last())
为了避免在检索位置不存在的元素时出现异常,请使用 elementAt()
的安全变体:
- 当指定位置超出集合范围时,
elementAtOrNull()
返回 null。 -
elementAtOrElse()
还接受一个 lambda 表达式,该表达式能将一个Int
参数映射为一个集合元素类型的实例。 当使用一个越界位置来调用时,elementAtOrElse()
返回对给定值调用该 lambda 表达式的结果。
val numbers = listOf("one", "two", "three", "four", "five")
println(numbers.elementAtOrNull(5))
println(numbers.elementAtOrElse(5) { index -> "The value for index $index is undefined"})
18、取单个元素-按条件取
函数 first()
和 last()
还可以让你在集合中搜索与给定谓词匹配的元素。 当你使用测试集合元素的谓词调用 first()
时,你会得到对其调用谓词产生 true
的第一个元素。 反过来,带有一个谓词的 last()
返回与其匹配的最后一个元素。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.first { it.length > 3 })
println(numbers.last { it.startsWith("f") })
如果没有元素与谓词匹配,两个函数都会抛异常。 为了避免它们,请改用 firstOrNull()
和 lastOrNull()
:如果找不到匹配的元素,它们将返回 null
。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.firstOrNull { it.length > 6 })
19、随机取元素
如果需要检索集合的一个随机元素,那么请调用 random()
函数。 你可以不带参数或者使用一个 Random
对象作为随机源来调用它。
val numbers = listOf(1, 2, 3, 4)
println(numbers.random())
20、检测存在与否
如需检查集合中某个元素的存在,可以使用 contains()
函数。 如果存在一个集合元素等于(equals()
)函数参数,那么它返回 true
。 你可以使用 in
关键字以操作符的形式调用 contains()
。
如需一次检查多个实例的存在,可以使用这些实例的集合作为参数调用 containsAll()
。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.contains("four"))
println("zero" in numbers)
println(numbers.containsAll(listOf("four", "two")))
println(numbers.containsAll(listOf("one", "zero")))
此外,你可以通过调用 isEmpty()
和 isNotEmpty()
来检查集合中是否包含任何元素。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.isEmpty())
println(numbers.isNotEmpty())
val empty = emptyList()
println(empty.isEmpty())
println(empty.isNotEmpty())
21、 自然顺序
基本的函数 sorted()
和 sortedDescending()
返回集合的元素,这些元素按照其自然顺序升序和降序排序。 这些函数适用于 Comparable
元素的集合。
val numbers = listOf("one", "two", "three", "four")
println("Sorted ascending: ${numbers.sorted()}")
println("Sorted descending: ${numbers.sortedDescending()}")
22、 自定义顺序
为了按照自定义顺序排序或者对不可比较对象排序,可以使用函数 sortedBy()
和 sortedByDescending()
。 它们接受一个将集合元素映射为 Comparable
值的选择器函数,并以该值的自然顺序对集合排序。
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numbers.sortedBy { it.length }
println("Sorted by length ascending: $sortedNumbers")
val sortedByLast = numbers.sortedByDescending { it.last() }
println("Sorted by the last letter descending: $sortedByLast")
23、倒序
你可以使用 reversed()
函数以相反的顺序检索集合。
val numbers = listOf("one", "two", "three", "four")
println(numbers.reversed())
reversed()
返回带有元素副本的新集合。 因此,如果你之后改变了原始集合,这并不会影响先前获得的 reversed()
的结果。
另一个反向函数——asReversed()
——返回相同集合实例的一个反向视图,因此,如果原始列表不会发生变化,那么它会比 reversed()
更轻量,更合适。
val numbers = listOf("one", "two", "three", "four")
val reversedNumbers = numbers.asReversed()
println(reversedNumbers)
24、 随机顺序
最后,shuffled()
函数返回一个包含了以随机顺序排序的集合元素的新的 List
。 你可以不带参数或者使用 Random
对象来调用它。
val numbers = listOf("one", "two", "three", "four")
println(numbers.shuffled())
25、集合聚合操作
Kotlin 集合包含用于常用的 聚合操作 (基于集合内容返回单个值的操作)的函数 。 其中大多数是众所周知的,并且其工作方式与在其他语言中相同。
-
min()
与max()
分别返回最小和最大的元素; -
average()
返回数字集合中元素的平均值; -
sum()
返回数字集合中元素的总和; -
count()
返回集合中元素的数量;
val numbers = listOf(6, 42, 10, 4)
println("Count: ${numbers.count()}")
println("Max: ${numbers.max()}")
println("Min: ${numbers.min()}")
println("Average: ${numbers.average()}")
println("Sum: ${numbers.sum()}")
26、集合写操作
可变集合支持更改集合内容的操作,例如添加或删除元素。 在此页面上,我们将描述实现 MutableCollection
的所有写操作。 有关 List
与 Map
可用的更多特定操作,请分别参见 List 相关操作与 Map 相关操作。
** 添加元素**
- 要将单个元素添加到列表或集合,请使用
add()
函数。指定的对象将添加到集合的末尾。 -
addAll()
将参数对象的每个元素添加到列表或集合中。 - 你还可以使用
plus
运算符 -plusAssign
(+=
) 添加元素。 当应用于可变集合时,+=
将第二个操作数(一个元素或另一个集合)追加到集合的末尾。
//第一种
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
println(numbers)
//第三种
val numbers = mutableListOf("one", "two")
numbers += "three"
println(numbers)
numbers += listOf("four", "five")
println(numbers)
27、删除元素
若要从可变集合中移除元素,请使用 remove()
函数。 remove()
接受元素值,并删除该值的一个匹配项。
val numbers = mutableListOf(1, 2, 3, 4, 3)
numbers.remove(3) // 删除了第一个 `3`
println(numbers)
numbers.remove(5) // 什么都没删除
println(numbers)
要一次删除多个元素,有以下函数:
-
removeAll()
移除参数集合中存在的所有元素。 或者,你可以用谓词作为参数来调用它;在这种情况下,函数移除谓词产生true
的所有元素。 -
retainAll()
与removeAll()
相反:它移除除参数集合中的元素之外的所有元素。 当与谓词一起使用时,它只留下与之匹配的元素。 -
clear()
从列表中移除所有元素并将其置空。
从集合中移除元素的另一种方法是使用 minusAssign
(-=
) ——原地修改版的 minus
操作符。 minus
操作符。 第二个参数可以是元素类型的单个实例或另一个集合。 右边是单个元素时,-=
会移除它的第一个匹配项。 反过来,如果它是一个集合,那么它的所有元素的每次出现都会删除。 例如,如果列表包含重复的元素,它们将被同时删除。 第二个操作数可以包含集合中不存在的元素。这些元素不会影响操作的执行。
val numbers = mutableListOf("one", "two", "three", "three", "four")
numbers -= "three"
println(numbers)
numbers -= listOf("four", "five")
//numbers -= listOf("four") // 与上述相同
println(numbers)