关于kotlin:Kotlin-Vocabulary-Collection-和-Sequence

35次阅读

共计 3066 个字符,预计需要花费 8 分钟才能阅读完成。

在很多场景中咱们会应用到汇合,Kotlin 规范库 (Kotlin Standard Library) 中提供了十分多杰出的对于汇合的实用函数。其中,Kotlin 提供了基于不同执行形式的两种汇合类型: 立刻执行 (eagerly) 的 Collection 类型, 提早执行 (lazily) 的 Sequence 类型。本篇文章将向您介绍两者的区别,并向您介绍这两种类型别离该在哪种状况下应用,以及它们的性能体现。获取更多相干信息能够查看如下视频:

https://www.bilibili.com/vide…

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)
}

Sequence (也称序列) 是提早执行的,它有两种类型: 两头操作 (intermediate) 和 末端操作 (terminal)。两头操作不会立刻执行,它们只是被存储起来,仅当末端操作被调用时,才会依照程序在每个元素上执行两头操作,而后执行末端操作。两头操作 (比方 map、distinct、groupBy 等) 会返回另一个 Sequence,而末端操作 (比方 first、toList、count 等) 则不会。

Sequence 是不会保留对汇合我的项目的援用的。它基于原始汇合的迭代器 (iterator) 创立,并且保留要执行的所有两头操作的援用。

与在 Collection 中执行转换操作不同,Sequence 执行的两头转换不是内联函数,因为内联函数无奈存储,而 Sequence 须要存储它们。咱们能够通过下列代码看到像 map 这样的两头操作是如何实现的,能够看到转换函数会存储在一个新的 Sequence 实例中:

public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R>{return TransformingSequence(this, transform)
}

例如 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.”)
}

如果察看 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())
   }
   …

无论您应用 Collection 还是 Sequence,Kotlin 规范库都提供了相似于 find、filter、groupBy 等一系列操作,在应用它们之前,您得确保 理解这些操作。

Collection 和 Sequence 如何抉择

假如咱们有一个列表,存储了许多不同形态的对象,咱们心愿将列表中形态的色彩变成黄色,而后获取列表中的第一个正方形。

运行 Collection 和 Sequence 代码

咱们来看一下针对 Collection 和 Sequence 的各个操作是如何执行以及何时执行的。

Collections

  • 调用 map 时 —— 一个新的 ArrayList 会被创立。咱们遍历了初始 Collection 中所有我的项目,复制原始的对象,而后更改它的色彩,再将其增加到新的列表中;
  • 调用 first 时 —— 遍历每一个我的项目,直到找到第一个正方形。

Sequences

  • asSequence —— 基于原始汇合的迭代器创立一个 Sequence;
  • 调用 map 时 —— Sequence 会将转换操作的信息存储到一个列表中,该列表只会存储要执行的操作,并不会执行这些操作;
  • 调用 first 时 —— 这是一个末端操作,所以会将两头操作作用到汇合中的每个元素。咱们遍历初始汇合,对每个元素执行 map 操作,而后继续执行 first 操作,当遍历到第二个元素时,发现它合乎咱们的要求,所以就无需在残余的元素中进行 map 操作了。

应用 Sequence 时不会去创立两头汇合,因为我的项目会被一一执行,map 操作只会作用到局部输出上。

Collection 和 Sequence 的比照 — 立刻解决和提早解决间的比照

性能

转换的程序

无论您应用 Collection 还是 Sequence,转换的程序都很重要。在下面的例子中,first 不须要先在 map 之后进行操作,因为 first 不须要 map 操作的后果就可能执行。如果咱们颠倒业务逻辑的程序,先把 first 作用到 Collection 上,再对后果执行转换,那么咱们只会创立一个新的对象 —— 一个黄色的正方形。当应用 Sequence 时,会防止创立两个新对象,而当应用 Collection 时则会防止创立整个列表。

转换程序的重要性 — 防止无用操作

因为末端操作能够提前对工作进行解决,而两头操作会提早进行解决,所以在某些状况下,相比于 Collection,Sequence 能够防止一些无用操作。应用时,请确保查看了转换程序以及它们的依赖关系。

内联和大数据集所带来的影响

Collection 的操作应用了内联函数,所以解决所用到的字节码以及传递给它的 lambda 字节码都会进行内联操作。而 Sequence 不应用内联函数,因而,它会为每个操作创立新的 Function 对象。

另外,Collection 会为每个转换操作创立一个新的列表,而 Sequence 仅仅是保留对转换函数的援用。

当对 数据量小 的 Collection 执行 1 到 2 个操作时,下面所说的差别并不会带来什么样的影响,所以这种状况下应用 Collection 是没问题的。而当 列表数据很大 时,两头汇合的创立会很耗费资源,这种状况下就应该应用 Sequence

可怜的是,我不晓得有什么样的基准测试可能帮忙咱们更好地摸索出具体不同大小的汇合或者操作链才会对 Collection 和 Sequence 产生影响。

综上所述,Collection 会立刻执行对数据的操作,而 Sequence 则是提早执行。依据要解决的数据量大小,抉择最合适的一个: 数据量小,则应用 Collection,数据量大,则应用 Sequence,另外,需注意操作程序带来的影响。

正文完
 0