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

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

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 } print("before sum ") val sum = seqMapped.sum() 

    它打印:

     before sum 1 2 

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

    一般来说,没有什么好的方法来确定什么时候需要它,所以在Kotlin中stdlib懒惰是明确的,并提取到Sequence<T>接口,以避免在所有的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>是非惰性的,这个解决方案很好地展示了它:在大多数情况下,它具有良好的参考局部性,从而利用CPU缓存,预测,预取等优势,收集仍然工作得很好,并在小集合的简单情况下表现更好。

    如果您需要对评估流水线进行更多的控制,则会使用Iterable<T>.asSequence()函数显式转换为惰性序列。

Interesting Posts