在 Java 类库中有一套相当完整的容器集合类来持有对象。Kotlin 没有去重复造轮子(Scala 则是自己实现了一套集合类框架),而是在 Java 类库的基础上进行了改造和扩展,引入了不可变集合类,同时扩展了大量方便实用的功能,这些功能的 API 都在 kotlin.collections 包下面。
另外,在 Kotlin 的集合类中不仅仅能持有普通对象,而且能够持有函数类型的变量。例如下面是一个持有两个函数的集合类:
val funList: List<(Int) -> Boolean> = listOf(
{ it -> it % 2 == 0 }, // 第一个函数为 { it -> it % 2 == 0 }
{ it -> it % 2 == 1 }) // 第二个函数为 { it -> it % 1 == 1}
复制代码
其中,(Int)->Boolean 是一个从 Int 映射到 Boolean 的函数。而这个时候,我们可以在代码里选择调用哪个函数:
fun main() {
val list = listOf(1, 2, 3, 4, 5, 6, 7)
list.filter(funList[0]) // [2, 4, 6]
list.filter(funList[1]) // [1, 3, 5, 7]
}
复制代码
是不是感觉很有意思?这就是面向对象范式混合函数式编程的自由乐趣吧!
集合类概述
Kotlin 的集合类分为:可变集合类(Mutable)和 不可变集合类 (Immutable)。
下面是 Kotlin 中集合类接口的结构层次:
如果我们从数据结构的本质来看,其实 List 中的下标就是 Key,只不过 Key 是有序的 Int 类型,所以说 List 也可以说是一种特殊的 Map 数据结构。而 Set 也是 Key 为 Int 类型,但是 Value 值是不能重复的特殊 Map。
不可变集合类
List 列表分为只读不可变的 List 和可变 MutableList(可写入、删除数据)。
Set 集也分为不可变 Set 和可变 MutableSet(可写入、删除数据)。
Map 也分为只读 Map 和可变 MutableMap(可写入、删除数据)。
创建集合类
Kotlin 中分别使用 listOf()、setOf()、mapOf() 函数创建不可变的 List 列表容器、Set 集容器、Map 映射容器;使用 mutableListOf()、mutableSetOf()、mutableMap() 函数来创建可变的 MutableList 列表容器、MutableSet 集容器、MutableMap 映射容器。
代码示例如下:
val list = listOf(1, 2, 3, 4, 5, 6, 7) // 创建不可变的list
val mutableList = mutableListOf("a", "b", "c") //创建可变MutableList
val set = setOf(1, 2, 3, 4, 5, 6, 7) // 创建不可变 Set
val mutableSet = mutableSetOf("a", "b", "c") //创建可变的MutableSet
val map = mapOf(1 to "a", 2 to "b", 3 to "c") //创建不可变Map
val mutableMap = mutableMapOf(1 to "X", 2 to "Y", 3 to "Z")
复制代码
遍历集合中的元素
List、Set 类继承了 Iterable 接口,里面扩展了 forEach 函数来迭代遍历元素;同样,Map 接口中也扩展了 forEach 函数来迭代遍历元素。
list.forEach {
println(it)
}
set.forEach {
println(it)
}
map.forEach {
println("K = ${it.key}, V = ${it.value}")
}
复制代码
其中 forEach() 函数签名如下:
@kotlin.internal.HidesMembers
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
for (element in this) action(element)
}
@kotlin.internal.HidesMembers
public inline fun <K, V> Map<out K, V>.forEach(action: (Map.Entry<K, V>) -> Unit): Unit {
for (element in this) action(element)
}
复制代码
我们看到,在 Iteable 和 Map 中,forEach 函数都是一个内联 inline 函数。
另外,如果我们想在迭代遍历元素的时候访问 index 下标,在 List 和 Set 中可以使用下面的 forEachIndexed 函数
list.forEachIndexed { index, value -> // 带下标 index 来遍历 List
println("list index = ${index}, value = $value")
}
set.forEachIndexed { index, value -> // 带下标 index 来遍历 Set
println("set index = ${index}, value = ${value}")
}
复制代码
其中,第一个参数是 index,第二个参数是 value。这里的 forEachIndexed 函数签名如下:
public inline fun <T> Iterable<T>.forEachIndexed(action: (index: Int, T) -> Unit): Unit {
var index = 0
for (item in this) action(checkIndexOverflow(index++), item)
}
复制代码
映射函数
使用 map 函数,可以把集合中的元素依次使用给定的转换函数进行映射操作,元素映射之后的新值会存入一个新的集合中,并返回这个新集合。
在 List、Set 继承的 Iterable 接口和 Map 接口中,都提供了这个 map 函数。使用 map 函数的代码示例如下:
val list = listOf(1, 2, 3, 4, 5, 6, 7)
val set = listOf(1, 2, 3, 4, 5, 6, 7)
val map = mapOf(1 to "a", 2 to "b", 3 to "c")
list.map { it * it } // map 函数对每个元素进行乘方操作,返回 [1, 4, 5, 16, 25, 36, 49]
set.map { it + 1 } // map 函数对每个元素进行加 1 操作,返回 [2, 3, 4, 5, 6, 7, 8]
map.map { it.value + "$" } // map 函数对每个元素加上字符 $,返回 [a$, b$, c$]
复制代码
map 函数的签名如下:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>
public inline fun <K, V, R> Map<out K, V>.map(transform: (Map.Entry<K, V>) -> R): List<R>
复制代码
这里 R 类型是映射之后的类型,我们也可以传入一个 List:
val strList = listOf("a", "b", "c")
// 每个元素 it 映射之后返回一个 List,这个 List 中有四个元素
// 分别是: it + 1, it + 2, it + 3, it + 4
strList.map { it -> listOf(it + 1, it + 2, it + 3, it + 4) }
复制代码
这个时候,返回值的类型将是 List,也就是一个 List 里面嵌套一个 List,上面代码的返回结果如下:
[[a1, a2, a3, a4], [b1, b2, b3, b4], [c1, c2, c3, c4]]
复制代码
Kotlin 中还提供了一个 flatten() 函数,效果是把嵌套的 List 结构 “平铺”,变成一层的结构,代码示例如下:
strList.map { it -> listOf(it + 1, it + 2, it + 3, it + 4) }.flatten()
复制代码
输入如下:
[a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4]
复制代码
flatMap 函数是 map 和 flat 两个函数的 “复合逻辑”,代码示例如下:
flatMap = strList.flatMap { it -> listOf(it + 1, it + 2, it + 3, it + 4) }
复制代码
同样输入如下:
[a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4]
复制代码
过滤函数
如果我们想要过滤出年龄大于等于 18 的学生,代码可以这样写:
studentList.filter{ it.age >= 18}
复制代码
另外,如果想要通过访问下标来过滤,可以使用 filterIndexed() 函数:
val list = listOf(1, 2, 3, 4, 5, 6, 7)
list.filterIndexed { index, it -> index % 2 == 9 && it > 3 } // 返回 [5, 7]
复制代码
filterIndexed() 函数签名如下:
public inline fun <T> Iterable<T>.filterIndexed(predicate: (index: Int, T) -> Boolean): List<T>
复制代码
排序函数
Kotlin 集合类中提供了倒序排列集合元素的函数 reversed(),代码示例如下:
val list = listOf(1, 2, 3, 4, 5, 6, 7)
list.reversed() //倒序函数,返回 [7, 6, 5, 4, 3, 2, 1]
复制代码
这个 Iterable 的扩展函数 reversed() 是直接调用的 java.util.Collections.reverse() 方法。其中相关代码如下:
public fun <T> Iterable<T>.reversed(): List<T> {
if (this is Collection && size <= 1) return toList()
val list = toMutableList()
list.reverse()
return list
}
复制代码
升序排序函数是 sorted(),实例代码如下:
list.sorted()
复制代码
Kotlin 中的这个 sorted() 函数也是直接调用 Java 的 API 来实现的,相关代码如下:
public fun <T : Comparable<T>> Iterable<T>.sorted(): List<T> {
if (this is Collection) {
if (size <= 1) return this.toList()
@Suppress("UNCHECKED_CAST")
return (toTypedArray<Comparable<T>>() as Array<T>).apply { sort() }.asList()
}
return toMutableList().apply { sort() }
}
复制代码
其背后调用的是 Java.util.Arrays.sort() 方法。
Kotlin 的集合类中还提供了许多功能丰富的 API,更多内容可以参考方法 API 文档。