Kotlin的Iterable和Sequence看起来完全一样。 为什么需要两种types?
这两个接口都只定义了一种方法
public operator fun iterator(): Iterator
文件说Sequence
是懒惰的。 但是也不是可以Iterable
懒惰(除非有一个Collection
支持)?
关键的区别在于Iterable
和Sequence
的stdlib扩展函数的语义和实现。
-
对于
Sequence
,扩展函数在可能的情况下执行延迟,类似于Java Streams 中间操作。 例如,Sequence
返回另一个.map { ... } Sequence
,实际上并不处理这些项目,直到调用像toList
或fold
这样的终端操作。考虑这个代码:
val seq = sequenceOf(1, 2) val seqMapped: Sequence
= seq.map { print("$it "); it * it } print("before sum ") val sum = seqMapped.sum() 它打印:
before sum 1 2
当您想要尽可能减少在终端操作中完成的工作时,
Sequence
用于延迟使用和高效的流水线,与Java Streams相同。 然而,懒惰引入了一些开销,这对于较小集合的常见简单转换是不可取的,并且使得它们性能较差。一般来说,没有什么好的方法来确定什么时候需要它,所以在Kotlin stdlib中,懒惰是明确的,并且提取到
Sequence
接口,以避免在所有的Iterable
默认使用它。 -
对于
Iterable
,相反,具有中间操作语义的扩展函数急切地工作,立即处理项目并返回另一个Iterable
。 例如,Iterable
返回一个带有映射结果的.map { ... } List
。Iterable的等效代码:
val lst = listOf(1, 2) val lstMapped: List
= lst.map { print("$it "); it * it } print("before sum ") val sum = lstMapped.sum() 这打印出来:
1 2 before sum
如上所述,默认情况下,
Iterable
是非惰性的,这个解决方案很好地展示了它:在大多数情况下,它具有良好的参考局部性,从而利用CPU缓存,预测,预取等优势,收集仍然工作得很好,并在小集合的简单情况下表现更好。如果您需要对评估管道进行更多的控制,则可以使用
Iterable
函数显式转换为惰性序列。.asSequence()
完成热键的回答:
注意Sequence和Iterable迭代如何思考你的元素是很重要的:
序列示例:
list.asSequence() .filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") }
记录结果:
filter – 地图 – 每个; filter – 映射 – 每个
可重用的例子:
list.filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") }
filter – filter – 地图 – 地图 – 每个 – 每个