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

问题描述 投票:71回答:3

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

public operator fun iterator(): Iterator<T>

文档说Sequence意味着很懒。但是Iterable也不懒惰吗(除非有Collection支持)?

kotlin iterable lazy-sequences
3个回答
118
投票

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

  • 对于Sequence<T>,扩展功能在可能的情况下延迟执行,类似于Java Streams [[intermediate操作。例如,Sequence<T>.map { ... }返回另一个Sequence<T>.map { ... },在调用诸如Sequence<R>toList之类的terminal操作之前,实际上不会处理这些项目。考虑此代码:

    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

    当您想要尽可能减少在
    terminal
    操作中完成的工作时,

    before sum 1 2 用于延迟使用和有效的流水线操作,与Java Streams相同。但是,懒惰会带来一些开销,这对于较小的collection的常见简单转换是不希望的,并且会使它们的性能降低。 [通常,没有确定何时需要它的好方法,因此在Kotlin stdlib中,懒惰是明确的,并被提取到Sequence<T>接口,以避免默认情况下在所有Sequence<T>上使用它。

    ] >

  • 对于Iterable,相反,具有

    intermediate

    操作语义的扩展功能很忙,立即处理这些项目并返回另一个Iterable<T>。例如,Iterable返回包含映射结果的Iterable<T>.map { ... }
  • 等效于Iterable的代码:

    Iterable<T>.map { ... }

此打印输出:

List<R>

如上所述,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
,因此可以利用CPU缓存,预测,预取等优势,甚至可以多次复制一个集合仍然可以很好地工作,并且在简单的情况下使用小集合也可以表现更好。

如果需要对评估管道进行更多控制,请使用Iterable<T>函数将其显式转换为惰性序列。

完成热键的答案:

重要的是要注意Sequence和Iterable如何遍历整个元素:

序列示例:

locality of reference

记录结果:

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

可重复的示例:

Iterable<T>.asSequence()

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

Iterable<T>.asSequence()映射到计算机上的 list.asSequence() .filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") } 接口 list.filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") } ,并通过常用的集合(例如List或组。评估这些上的集合扩展功能急切地,这意味着它们都会立即处理其中的所有元素他们的输入,并返回包含结果的新集合。

这是一个使用集合函数获取列表中年龄至少21岁的前五个人的名字:

Iterable

目标平台:在kotlin v。1.3.61上运行的JVM首先,检查年龄为列表中的每个人完成的操作,结果放入全新清单。然后,对每个人的姓名进行映射仍在过滤器运算符之后但仍留在其中的人另一个新列表(现在是java.lang.Iterable)。最后,有一个创建的最后一个新列表包含该列表的前五个元素上一个列表。

[相反,Sequence是Kotlin中代表懒惰的新概念评估值的集合。相同的集合扩展是可用于JVM界面,但这些会立即返回表示日期的已处理状态的序列实例,但是而不实际处理任何元素。要开始处理,val people: List<Person> = getPeople() val allowedEntrance = people .filter { it.age >= 21 } .map { it.name } .take(5) 必须由终端操作员终止,这些是基本上是对序列的要求,以实现其数据以某种具体形式表示。例如List<String>Sequence,和Sequence,仅举几例。当这些被调用时,只有最少需要的元素数量将被处理以生成要求的结果。

将现有集合转换为序列非常漂亮直截了当,您只需要使用toList扩展名即可。如上面提到的,还需要添加一个终端运算符,否则该序列将永远不会进行任何处理(再次,懒惰!)。

toSet

目标平台:在kotlin v。1.3.61上运行的JVM。序列中的每个人物实例都会被检查年龄,如果他们通过了,提取了他们的名字,然后将其添加到结果列表。对原始列表中的每个人重复此操作直到找到五个人。至此,toList函数返回一个列表,sum中的其他人不已处理。

[Sequence还有其他一些功能:它可以包含无限数量的项目。从这个角度来看,这很有意义运算符以他们的方式工作-无限运算符如果它热切地工作,序列将永远不会返回。

例如,这是一个序列,它将产生2根据其终端操作员的要求(忽略此事实会很快溢出):

asSequence

您可以找到更多val people: List<Person> = getPeople()
val allowedEntrance = people.asSequence()
    .filter { it.age >= 21 }
    .map { it.name }
    .take(5)
    .toList()

40
投票
完成热键的答案:

重要的是要注意Sequence和Iterable如何遍历整个元素:


0
投票
Iterable<T>.asSequence()映射到计算机上的 list.asSequence() .filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") } 接口 list.filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") } ,并通过常用的集合(例如List或组。评估这些上的集合扩展功能急切地,这意味着它们都会立即处理其中的所有元素他们的输入,并返回包含结果的新集合。

这是一个使用集合函数获取列表中年龄至少21岁的前五个人的名字:

© www.soinside.com 2019 - 2024. All rights reserved.