这两个接口(interface)仅定义一种方法

public operator fun iterator(): Iterator<T>

文档说Sequence是懒惰的。但是Iterable是否也懒惰(除非有Collection支持)?

最佳答案

关键区别在于Iterable<T>Sequence<T>的语义和stdlib扩展功能的实现。

  • 对于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
    

    它打印:
    before sum 1 2
    

    当您想要尽可能减少终端操作中完成的工作时,Sequence<T>用于懒惰使用和高效的流水线操作,与Java Streams相同。但是,懒惰会带来一些开销,这对于较小的collection的常见简单转换是不希望的,并且会使它们的性能降低。

    通常,没有确定何时需要它的好方法,因此在Kotlin中,将stdlib懒惰明确化并提取到Sequence<T>接口(interface),以避免默认情况下在所有Iterable上使用它。
  • 相反,对于Iterable<T>来说,具有中间操作语义的扩展功能很忙,可以立即处理这些项目并返回另一个Iterable。例如, Iterable<T>.map { ... } 返回带有映射结果的List<R>

    等效的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缓存,预测,预取等优势,因此即使对集合进行多次复制也仍然足够好并在简单的情况下(小收藏集)表现更好。

    如果需要对评估管道进行更多控制,则可以使用 Iterable<T>.asSequence() 函数将其显式转换为惰性序列。
  • 关于kotlin - Kotlin的Iterable和Sequence看起来完全一样。为什么需要两种类型?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44703690/

    10-14 17:34