在很多场景中我们会使用到集合,Kotlin 标准库 (Kotlin Standard Library) 中提供了非常多出色的关于集合的实用函数。其中,Kotlin 提供了基于不同执行方式的两种集合类型: 立即执行(eagerly) 的 Collection 类型,延迟执行 (lazily) 的 Sequence 类型。本篇文章将向您介绍两者的区别,并向您介绍这两种类型分别该在哪种情况下使用,以及它们的性能表现。
- 腾讯视频链接https://v.qq.com/x/page/d09360fuh1s.html
- Bilibili 视频链接https://www.bilibili.com/video/av97781019/
Collection 和 Sequence 的对比
欢迎关注并投稿
Collection (也称集合) 是在每次操作时立即执行的,执行结果会存储到一个新的集合中。作用于 Collection 的转换操作是内联函数。例如,map 的实现方式,可以看到它是一个创建了新 ArrayList 的内联函数:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> { return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)}
内联函数
https://kotlinlang.org/docs/reference/inline-functions.html
map
https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/common/src/generated/_Collections.kt#L1312
public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R>{ return TransformingSequence(this, transform)}
map
https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/common/src/generated/_Sequences.kt#L860
例如 first 这样的末端操作,会对 Sequence 中的元素进行遍历,直到预置条件匹配为止。
public inline fun <T> Sequence<T>.first(predicate: (T) -> Boolean): T { for (element in this) if (predicate(element)) return element throw NoSuchElementException(“Sequence contains no element matching the predicate.”)}
first
https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/common/src/generated/_Sequences.kt#L117
如果观察 TransformingSequence 这样的类型是如何实现的,我们会发现在迭代器上调用 next 时,转换存储操作也一并被应用。
internal class TransformingIndexedSequence<T, R> constructor(private val sequence: Sequence<T>, private val transformer: (Int, T) -> R) : Sequence<R> {override fun iterator(): Iterator<R> = object : Iterator<R> { … override fun next(): R { return transformer(checkIndexOverflow(index++), iterator.next()) } …}
- 了解这些操作https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/#functions
欢迎关注并投稿
Collection 和 Sequence 如何选择
Collections
调用 map 时 —— 一个新的 ArrayList 会被创建。我们遍历了初始 Collection 中所有项目,复制原始的对象,然后更改它的颜色,再将其添加到新的列表中;
调用 first 时 —— 遍历每一个项目,直到找到第一个正方形。
asSequence —— 基于原始集合的迭代器创建一个 Sequence;
调用 map 时 —— Sequence 会将转换操作的信息存储到一个列表中,该列表只会存储要执行的操作,并不会执行这些操作;
调用 first 时 —— 这是一个末端操作,所以会将中间操作作用到集合中的每个元素。我们遍历初始集合,对每个元素执行 map 操作,然后继续执行 first 操作,当遍历到第二个元素时,发现它符合我们的要求,所以就无需在剩余的元素中进行 map 操作了。
性能
转换的顺序
因为末端操作可以提前对任务进行处理,而中间操作会延迟进行处理,所以在某些情况下,相比于 Collection,Sequence 可以避免一些无用操作。使用时,请确保检查了转换顺序以及它们的依赖关系。
Collection 的操作使用了内联函数,所以处理所用到的字节码以及传递给它的 lambda 字节码都会进行内联操作。而 Sequence 不使用内联函数,因此,它会为每个操作创建新的 Function 对象。
欢迎关注并投稿