阅读 390

Kotlin - 集合类

在 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 中集合类接口的结构层次:

image.png

如果我们从数据结构的本质来看,其实 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 文档。

文章分类
后端
文章标签