Kotlin 的 Iterable 和 Sequence 看起来完全一样.为什么需要两种类型? [英] Kotlin's Iterable and Sequence look exactly same. Why are two types required?

查看:28
本文介绍了Kotlin 的 Iterable 和 Sequence 看起来完全一样.为什么需要两种类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这两个接口只定义了一个方法

Both of these interfaces define only one method

public operator fun iterator(): Iterator<T>

文档说 Sequence 是为了偷懒.但是 Iterable 是不是也很懒惰(除非有 Collection 支持)?

Documentation says Sequence is meant to be lazy. But isn't Iterable lazy too (unless backed by a Collection)?

推荐答案

关键区别在于 IterableSequence.

The key difference lies in the semantics and the implementation of the stdlib extension functions for Iterable<T> and Sequence<T>.

  • 对于Sequence,扩展函数在可能的情况下延迟执行,类似于Java Streams中间操作.例如,Sequence.map { ... } 返回另一个 Sequence 并且在终端操作(如toList)之前不会实际处理这些项目或 fold 被调用.

  • For Sequence<T>, the extension functions perform lazily where possible, similarly to Java Streams intermediate operations. For example, Sequence<T>.map { ... } returns another Sequence<R> and does not actually process the items until a terminal operation like toList or fold is called.

考虑这个代码:

val seq = sequenceOf(1, 2)
val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
print("before sum ")
val sum = seqMapped.sum() // terminal

它打印:

before sum 1 2

Sequence 适用于惰性使用和高效流水线,当您想尽可能减少 终端 操作中完成的工作时,与 Java Streams 相同.然而,懒惰引入了一些开销,这对于较小集合的常见简单转换是不可取的,并且会降低它们的性能.

Sequence<T> is intended for lazy usage and efficient pipelining when you want to reduce the work done in terminal operations as much as possible, same to Java Streams. However, laziness introduces some overhead, which is undesirable for common simple transformations of smaller collections and makes them less performant.

一般来说,没有什么好的方法来确定什么时候需要它,所以在 Kotlin stdlib 中将惰性显式化并提取到 Sequence 接口,以避免在所有Iterables 默认情况下.

In general, there is no good way to determine when it is needed, so in Kotlin stdlib laziness is made explicit and extracted to the Sequence<T> interface to avoid using it on all the Iterables by default.

对于Iterable,相反,具有中间操作语义的扩展函数急切地工作,立即处理项目并返回另一个可迭代.例如,Iterable.map { ... } 返回一个List,其中包含映射结果.

For Iterable<T>, on contrary, the extension functions with intermediate operation semantics work eagerly, process the items right away and return another Iterable. For example, Iterable<T>.map { ... } returns a List<R> with the mapping results in it.

Iterable 的等效代码:

The equivalent code for Iterable:

val lst = listOf(1, 2)
val lstMapped: List<Int> = lst.map { print("$it "); it * it }
print("before sum ")
val sum = lstMapped.sum()

打印出来:

1 2 before sum

如上所述,Iterable<T> 默认情况下是非惰性的,这个解决方案很好地证明了自己:在大多数情况下它有很好的 locality of reference 从而利用 CPU 缓存、预测、预取等,这样即使集合的多次复制仍然足够好,并且在简单情况下表现更好小集合.

As said above, Iterable<T> is non-lazy by default, and this solution shows itself well: in most cases it has good locality of reference thus taking advantage of CPU cache, prediction, prefetching etc. so that even multiple copying of a collection still works good enough and performs better in simple cases with small collections.

如果您需要对评估管道进行更多控制,可以使用 Iterable.asSequence() 函数.

If you need more control over the evaluation pipeline, there is an explicit conversion to a lazy sequence with Iterable<T>.asSequence() function.

这篇关于Kotlin 的 Iterable 和 Sequence 看起来完全一样.为什么需要两种类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆