Kotlin - 确保收集操作在开始下一个之前完成
Kotlin - make sure a collection operation finishes before starting the next one
如果编写如下代码:
fun main(args: Array<String>) {
val letters = listOf("a", "b", "c", "d")
letters.map { print(it); it }.forEach { print(it) }
}
我得到这个输出:abcdabcd
但是,如果我将其更改为序列:
fun main(args: Array<String>) {
val letters = listOf("a", "b", "c", "d")
letters.asSequence().map { print(it); it }.forEach { print(it) }
}
我得到以下信息:aabbccdd
Kotlin 的序列似乎不能保证 "group operation"(例如 map
或 forEach
)会在另一个开始之前完成,但是当我们将这些操作应用于集合时,有保证操作一次完成。
我们如何保证 "group operation" 例如 map
或 forEach
在开始执行下一个链接 "group operation"?
How can we guarantee that a "group operation" such as map or forEach will be applied to all the elements in a collection or a sequence before starting to execute the next chained "group operation"?
行为取决于输入是 Collection
还是 Sequence
。
- 集合会将链中的单个操作应用于集合中的每个元素,然后再应用链中的下一个操作。这就是为什么您看到
"abcd"
一次然后又看到 "abcd"
的原因。
- 序列会将链中的每个操作应用于序列中的每个元素,然后再将所有操作应用于序列中的下一个元素。这就是为什么您会看到
"aa"
,然后是 "bb"
,等等
主要要理解的是 Kotlin 序列是 惰性的。每当您转换 Sequence
(例如通过 map
或其他函数)时,该转换也会 也 延迟应用。只有当您实际评估序列中的元素时(在您的情况下,这是由 forEach
完成的),才会应用转换。
如果您需要 Sequence
的 Collection
行为,您必须先将序列转换为集合。您可以调用 sequence.toList()
或以其他方式对其进行转换,但是无法从开箱即用的 Sequence
中获得 "eager" 行为。
让我们来看看这两种不同的场景。
当 letters
是 List<String>
列表并不懒惰。为了 map
一个 List
到另一个,我们必须遍历整个输入列表,转换每个元素,并构建输出列表。考虑到这一点...
第一个操作 map { print(it); it }
从输入中读取每个值,打印它,并创建一个新列表。它 returns 一个包含 "a", "b", "c", "d"
的列表并且程序打印了 "abcd"
.
第二个操作 forEach { print(it) }
遍历列表并打印每个元素。此操作完成后,程序再次打印 "abcd"
,总输出为 "abcdabcd"
.
当 letters
是 Sequence<String>
序列是惰性的。为了map
一个Sequence
到另一个,程序保留了转换的方法,等到别人确实需要一个值,才进行转换。所以...
第一个操作创建一个新的 TransformingSequence
,它包含原始序列和 print(it); return it
转换器。它 returns 这个新序列和程序还没有打印任何东西。
第二个操作是"terminal"(即它实际评估序列中的每个元素)。它遍历序列并打印每个元素。但请记住:我们迭代的序列是一个 TransformingSequence
本身打印每个元素!因此 forEach
调用中的第一个 print
操作打印 "aa"
,第二个打印 "bb"
,依此类推,总输出 "aabbccdd"
.
如果编写如下代码:
fun main(args: Array<String>) {
val letters = listOf("a", "b", "c", "d")
letters.map { print(it); it }.forEach { print(it) }
}
我得到这个输出:abcdabcd
但是,如果我将其更改为序列:
fun main(args: Array<String>) {
val letters = listOf("a", "b", "c", "d")
letters.asSequence().map { print(it); it }.forEach { print(it) }
}
我得到以下信息:aabbccdd
Kotlin 的序列似乎不能保证 "group operation"(例如 map
或 forEach
)会在另一个开始之前完成,但是当我们将这些操作应用于集合时,有保证操作一次完成。
我们如何保证 "group operation" 例如 map
或 forEach
在开始执行下一个链接 "group operation"?
How can we guarantee that a "group operation" such as map or forEach will be applied to all the elements in a collection or a sequence before starting to execute the next chained "group operation"?
行为取决于输入是 Collection
还是 Sequence
。
- 集合会将链中的单个操作应用于集合中的每个元素,然后再应用链中的下一个操作。这就是为什么您看到
"abcd"
一次然后又看到"abcd"
的原因。 - 序列会将链中的每个操作应用于序列中的每个元素,然后再将所有操作应用于序列中的下一个元素。这就是为什么您会看到
"aa"
,然后是"bb"
,等等
主要要理解的是 Kotlin 序列是 惰性的。每当您转换 Sequence
(例如通过 map
或其他函数)时,该转换也会 也 延迟应用。只有当您实际评估序列中的元素时(在您的情况下,这是由 forEach
完成的),才会应用转换。
如果您需要 Sequence
的 Collection
行为,您必须先将序列转换为集合。您可以调用 sequence.toList()
或以其他方式对其进行转换,但是无法从开箱即用的 Sequence
中获得 "eager" 行为。
让我们来看看这两种不同的场景。
当 letters
是 List<String>
列表并不懒惰。为了 map
一个 List
到另一个,我们必须遍历整个输入列表,转换每个元素,并构建输出列表。考虑到这一点...
第一个操作 map { print(it); it }
从输入中读取每个值,打印它,并创建一个新列表。它 returns 一个包含 "a", "b", "c", "d"
的列表并且程序打印了 "abcd"
.
第二个操作 forEach { print(it) }
遍历列表并打印每个元素。此操作完成后,程序再次打印 "abcd"
,总输出为 "abcdabcd"
.
当 letters
是 Sequence<String>
序列是惰性的。为了map
一个Sequence
到另一个,程序保留了转换的方法,等到别人确实需要一个值,才进行转换。所以...
第一个操作创建一个新的 TransformingSequence
,它包含原始序列和 print(it); return it
转换器。它 returns 这个新序列和程序还没有打印任何东西。
第二个操作是"terminal"(即它实际评估序列中的每个元素)。它遍历序列并打印每个元素。但请记住:我们迭代的序列是一个 TransformingSequence
本身打印每个元素!因此 forEach
调用中的第一个 print
操作打印 "aa"
,第二个打印 "bb"
,依此类推,总输出 "aabbccdd"
.