这两个接口仅定义一种方法
public operator fun iterator(): Iterator<T>
文档说Sequence
是懒惰的。但是,不是也Iterable
很懒(除非有a支持Collection
)?
这两个接口仅定义一种方法
public operator fun iterator(): Iterator<T>
文档说Sequence
是懒惰的。但是,不是也Iterable
很懒(除非有a支持Collection
)?
Answers:
关键的区别在于语义和的STDLIB扩展功能的实现Iterable<T>
和Sequence<T>
。
对于Sequence<T>
,扩展功能在可能的情况下延迟执行,类似于Java Streams中间操作。例如,Sequence<T>.map { ... }
返回另一个Sequence<R>
,并且直到调用诸如或的终端操作之前,它才真正处理项目。toList
fold
考虑以下代码:
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>
接口,以免Iterable
默认情况下在所有s上使用它。
对于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()
功能的惰性序列。
map
,像filter
和等函数的通用协定除了从源集合类型中获取以外,没有携带足够的信息来决定其他信息,并且由于大多数集合也是可迭代的,因此这不是“懒惰”的好标记,因为通常在任何地方。懒惰必须明确,以确保安全。
完成热键的答案:
重要的是要注意Sequence和Iterable如何遍历整个元素:
序列示例:
list.asSequence().filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
记录结果:
filter-Map-Each; 过滤器-地图-每个
可迭代的示例:
list.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
筛选器-筛选器-地图-地图-每个-每个
Iterable
映射到上的java.lang.Iterable
接口JVM
,并由常用集合(例如List或Set)实现。对这些扩展的集合扩展功能进行了热切的评估,这意味着它们都将立即处理其输入中的所有元素,并返回包含结果的新集合。这是一个使用集合函数来获取年龄在21岁以上的列表中前五个人的姓名的简单示例:
val people: List<Person> = getPeople() val allowedEntrance = people .filter { it.age >= 21 } .map { it.name } .take(5)
目标平台:JVM在kotlin v。1.3.61上运行。首先,对列表中的每个Person进行年龄检查,并将结果放入全新的列表中。然后,为每个在过滤器运算符之后留下的Person进行名称映射,最后出现在另一个新列表中(现在是
List<String>
)。最后,创建了最后一个新列表,以包含上一个列表的前五个元素。相反,Sequence是Kotlin中的一个新概念,代表了惰性评估的值集合。该
Sequence
接口可以使用相同的集合扩展,但是它们会立即返回Sequence实例,这些实例代表日期的已处理状态,但实际上并未处理任何元素。要开始处理,Sequence
必须由终端操作员终止,这基本上是对Sequence的请求,以具体化它具体表示的数据。示例包括toList
,toSet
和sum
,仅举几例。调用这些元素时,将仅处理最少数量的元素以产生所需的结果。将现有集合转换为Sequence非常简单,只需使用
asSequence
扩展名即可。如上所述,您还需要添加一个终端运算符,否则Sequence将永远不会进行任何处理(再次,懒惰!)。
val people: List<Person> = getPeople() val allowedEntrance = people.asSequence() .filter { it.age >= 21 } .map { it.name } .take(5) .toList()
目标平台:在kotlin v。1.3.61上运行的JVMR在这种情况下,将检查Sequence中的Person实例的年龄,如果通过,则提取其名称,然后将其添加到结果列表中。对原始列表中的每个人重复此操作,直到找到五个人。此时,toList函数将返回一个列表,并且
Sequence
不处理其中的其余人员。序列还具有其他功能:可以包含无限数量的项目。从这个角度来看,操作员按自己的方式工作是有道理的-无限序列上的操作员如果急于工作,就永远不会返回。
举例来说,下面的序列将生成其终端运算符所需的2的幂(忽略此事实会很快溢出):
generateSequence(1) { n -> n * 2 } .take(20) .forEach(::println)
你可以在这里找到更多。
Java
(大多数Guava
)粉丝来说,可能是一个很大的惊喜