Kotlin的Iterable和Sequence看起来完全一样.为什么需要两种类型?

Ven*_*aju 71 iterable lazy-sequences kotlin

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

public operator fun iterator(): Iterator<T>
Run Code Online (Sandbox Code Playgroud)

文档说Sequence是懒惰的.但是也不是Iterable懒惰(除非由a支持Collection)?

hot*_*key 117

关键的区别在于语义和stdlib扩展函数的实现Iterable<T>Sequence<T>.

  • 因为Sequence<T>,扩展函数尽可能延迟执行,类似于Java Streams 中间操作.例如,Sequence<T>.map { ... }返回另一个Sequence<R>,并且不实际处理的项目,直到一个终端等的操作toListfold将被调用.

    考虑以下代码:

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

    它打印:

    before sum 1 2
    
    Run Code Online (Sandbox Code Playgroud)

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

    一般来说,没有好的方法可以确定何时需要它,所以在Kotlin中,stdlib懒惰被明确地提取并提取到Sequence<T>界面以避免在Iterable默认情况下在所有s 上使用它.

  • 对于Iterable<T>上相反,扩展功能的中间操作语义工作急切,过程中的项目马上和返回另一个Iterable.例如,Iterable<T>.map { ... }返回List<R>带有映射结果的a .

    Iterable的等效代码:

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

    打印出:

    1 2 before sum
    
    Run Code Online (Sandbox Code Playgroud)

    如上所述,Iterable<T>默认情况下是非延迟的,并且这个解决方案很好地表现出来:在大多数情况下它具有良好的引用局部性,因此利用了CPU缓存,预测,预取等,因此即使多次复制集合仍然可以正常工作在小集合的简单情况下,足够并且表现更好.

    如果您需要对评估管道进行更多控制,则可以使用Iterable<T>.asSequence()函数显式转换为延迟序列.

  • 对于较小和更常用的集合,默认情况下,Lazy通常性能较差.如果利用CPU缓存等,副本可能比懒惰的eval更快.因此对于常见用例最好不要偷懒.不幸的是,像`map`,`filter`等函数的常见契约除了源集合类型之外没有足够的信息来决定,并且因为大多数集合也是可迭代的,所以这不是一个好的标记"懒惰"因为它通常在任何地方.懒惰必须明确是安全的. (8认同)
  • 可能是"Java"(主要是"番石榴")粉丝的一大惊喜 (3认同)
  • 此外,懒惰评估的常见缺陷是捕获上下文并将所得到的懒惰计算与所有捕获的本地及其所持有的内容一起存储在字段中.因此,很难调试内存泄漏. (2认同)

Lea*_*ira 38

完成热键的答案:

重要的是要注意Sequence和Iterable如何在整个元素中迭代:

顺序示例:

        list.asSequence()
            .filter { field ->
                Log.d("Filter", "filter")
                field.value > 0
            }.map {
                Log.d("Map", "Map")
            }.forEach {
                Log.d("Each", "Each")
            }
Run Code Online (Sandbox Code Playgroud)

记录结果:

过滤器 - 地图 - 每个; 过滤器 - 地图 - 每个

可重复的例子:

             list.filter { field ->
                    Log.d("Filter", "filter")
                    field.value > 0
                }.map {
                    Log.d("Map", "Map")
                }.forEach {
                    Log.d("Each", "Each")
                }
Run Code Online (Sandbox Code Playgroud)

过滤器 - 过滤器 - 地图 - 地图 - 每个 - 每个

  • 这是两者之间差异的一个很好的例子. (4认同)