何时在 Kotlin 中使用序列而不是列表?

rph*_*rph 1 kotlin

我见过的大多数 Kotlin 示例和实际代码库都是通过常规列表执行操作的。

data class Person(val name: String, val age: Int)

fun main() {
    val people = listOf(Person("John", 29), Person("Jane", 31))

    people.filter { it.age > 30 }.map { it.name }
}
Run Code Online (Sandbox Code Playgroud)

在现实世界的场景中,使用序列而不是列表更有意义,反之亦然?

people.asSequence().filter { it.age > 30 }.map { it.name }
Run Code Online (Sandbox Code Playgroud)

bro*_*oot 6

直觉告诉我们,序列的性能应该更好,因为它们专注于在处理下一个项目之前完全处理单个项目。集合的处理似乎是对资源的巨大浪费,因为我们必须在此过程中创建多个中间集合。

然而,现实却大不相同 - 两种解决方案都具有相当的性能,并且我相信潜在的差异实际上有利于集合(Kotlin 1.8.x)。有几个原因:

  • 集合处理是完全内联的,序列需要调用 lambda。
  • 集合的实现通常更简单,因此开销也更少。
  • 在某些情况下,例如map()我们预先知道结果列表的大小,因此我们可以为其分配空间。序列需要复制数据才能增长。

其中一些问题将来可以通过内联序列处理来解决。那么他们在性能方面通常应该更胜一筹。现在我想说集合是默认方法,我们可以在非常特定的情况下使用序列,例如:

  • 按需生成项目:生成器、从磁盘/网络加载、无限序列等。
  • 如果处理需要大量资源,则需要一些 I/O、大量内存等,我们可能希望在处理下一个项目之前完全处理单个项目。
  • 如果我们使用平面映射,然后使用过滤器,那么序列就不会同时将所有项目保留在内存中。例如,我们有一个包含 1000 个项目的列表,每个平面映射到 1000 个项目,然后我们对其进行过滤,平均每 1000 个项目仅保留一个项目。在使用序列时,我们在任何特定的情况下仅在内存中保留几千个项目时间。在使用集合时,我们必须创建一个包含一百万个项目的列表。
  • 如果我们需要观察每个项目而不是每个阶段的进度。

类似这样的例子可能还有更多。一般来说,如果您发现有理由逐一处理项目,那么序列正是允许这样做的。