Kotlin:流vs序列-为什么用多种方法做同一件事?

nnvyjq4y  于 2022-12-19  发布在  Kotlin
关注(0)|答案(2)|浏览(134)

在Kotlin有多种方法来做同一件事,这是为什么呢

val viaSequence = items.asSequence()
    .filter { it%2 == 0 }
    .map { it*2 }
    .toList()
println(viaSequence)

val viaIterable = items.asIterable()
    .filter { it%2 == 0 }
    .map { it*2 }
    .toList()
println(viaIterable)

val viaStream = items.stream()
    .filter { it%2 == 0 }
    .map { it*2 }
    .toList()
println(viaStream)

我知道下面的代码在每一步都创建了一个列表,这会增加GC的负载,因此应该避免:

items.filter { it%2 == 0 }.map { it*2 }
uplii1fm

uplii1fm1#

流来自于Java,Java中没有内联函数,因此流是在Java集合上使用这些函数链的唯一方法。Kotlin可以直接在Iterables上执行,这在很多情况下对性能更好,因为不需要创建中间的Stream对象。
Kotlin将Sequences作为Streams的替代方案,具有以下优点:

  • 它们使用null来表示缺少的项,而不是Optional。由于Kotlin的null安全特性,可以为null的值更容易使用。避免 Package 集合中的所有项也会提高性能。
  • 一些运算符和聚合函数要简洁得多,并且避免了使用泛型类型(比较Sequence.groupByStream.collect)。
  • 为Sequence提供了更多的运算符,通过减少中间步骤,可以带来性能优势和更简单的代码。
  • 许多终端操作符都是内联函数,因此它们省略了Stream需要的最后一个 Package 器。
  • sequence builder可以让你在一个协程中用简单的顺序语法创建一个复杂的惰性项目序列,非常强大。
  • 流需要Java 8或更高版本。这一点与Kotlin1.5和更高版本无关,因为Kotlin现在需要JDK 8或更高版本。

另一个答案提到了流的优点。
Nice article comparing them here.

s3fp2yjn

s3fp2yjn2#

你的三种变通办法之一与使用清单本身相同:

items.asIterable()
    .filter { it%2 == 0 }

在这里,你调用的filter函数就像你刚刚调用的items.filter一样。List本身就是一个Iterable,所以它是被调用的Iterable过滤器。这个过滤器查看所有可用的元素,并返回一个完整的列表。
所以问题是为什么我们同时有流和序列。
流是Java的一部分。许多终端操作产生Optional。其他操作,如toList(),将产生非空安全的平台类型,如List<Integer!>。另一方面,序列是Kotlin的本机类型,它们可以与Kotlin自己的编译时空安全特性一起使用。此外,序列在Kotlin的非JVM变体中可用。
Kotlin的设计者可能必须创建一个新的类,因为如果他们刚刚向Stream添加了新的操作,例如作为扩展函数,它们会与现有的名称冲突(例如max()在Java中返回Optional,这对Kotlin来说并不理想,但选择一个不同于自然名称max的名称也不理想)。
因此,在大多数情况下,您应该更喜欢序列,因为它们更符合Kotlin习惯用法。然而,Java Streams可以做一些序列尚不可用的事情(例如,SummaryStatistics或并行操作)。当您需要一个仅在Streams中可用的操作,但您有一个序列时,您可以使用asStream()将序列转换为流(反之亦然)。
Streams的另一个优点是,您可以使用IntStream之类的基元流,以避免不必要的装箱/取消装箱。

相关问题