Scala Stream vs Scala List vs Scala Sequence 有什么区别

what is the difference between Scala Stream vs Scala List vs Scala Sequence

我有一个场景,我以对象流的形式获取数据库数据。 在将其转换为一系列 Object 时,它需要时间。 我正在寻找花费更少时间的替代方案。

快速回答:Scala 流 已经是 Scala 序列,根本不需要转换。下面进一步解释...

A Scala 序列 (scala.collection.Seq) 只是任何以特定顺序存储元素序列的集合(顺序是任意的,但元素顺序不是'一旦定义就不会改变)。

A Scala list (scala.collection.immutable.List) 是 Seq 的子类,也是 scala.collection.Seq 的默认实现。也就是说,Seq(1, 2, 3) 被实现为 List(1, 2, 3)Lists 是严格的,因此在执行另一个操作之前,对列表的任何操作都会一个接一个地处理所有元素。

例如,在 Scala REPL 中考虑这个例子:

$ scala
Welcome to Scala 2.12.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.

scala> val xs = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)

scala> xs.map {x =>
     |   val newX = 2 * x
     |   println(s"Mapping value $x to $newX...")
     |   newX
     | }.foreach {x =>
     |   println(s"Printing value $x")
     | }
Mapping value 1 to 2...
Mapping value 2 to 4...
Mapping value 3 to 6...
Printing value 2
Printing value 4
Printing value 6

注意每个值是如何映射的,创建一个新列表 (List(2, 4, 6)),打印出新列表的任何值之前?

一个Scala stream(scala.collection.immutable.Stream)也是Seq的子类,但是lazy (或 non-strict),这意味着仅在需要时才获取流中的下一个值。它通常被称为 惰性列表

为了说明 StreamList 之间的区别,让我们重做那个例子:

scala> val xs = Stream(1, 2, 3)
xs: scala.collection.immutable.Stream[Int] = Stream(1, ?)

scala> xs.map {x =>
     |   val newX = 2 * x
     |   println(s"Mapping value $x to $newX...")
     |   newX
     | }.foreach {x =>
     |   println(s"Printing value $x")
     | }
Mapping value 1 to 2...
Printing value 2
Mapping value 2 to 4...
Printing value 4
Mapping value 3 to 6...
Printing value 6

请注意,对于 Stream,我们如何仅在前一个元素的 所有 操作完成后才处理下一个 map 操作? Map 操作仍然是 returns 一个新流 (Stream(2, 4, 6)),但仅在需要时才取值。

在任何特定情况下,Stream 是否比 List 表现更好取决于您尝试做什么。如果性能是您的主要目标,我建议您对代码进行基准测试(使用 ScalaMeter 等工具)以确定哪种类型最有效。

顺便说一句,由于 StreamList 都是 Seq 的子类,通常的做法是编写需要序列才能利用 Seq 的代码。这样,您可以提供一个 List 一个 Stream 任何其他 Seq 子类,而无需不必更改代码,也不必将列表、流等转换为序列。例如:

def doSomethingWithSeq[T](seq: Seq[T]) = {
  //
}

// This works!
val list = List(1, 2, 3)
doSomethingWithSeq(list)

// This works too!
val stream = Stream(4, 5, 6)
doSomethingWithSeq(stream)

已更新

对于 groupBy 操作,ListStream 的性能将非常相似。取决于它的使用方式,Stream 可能比 List 需要更少的内存,但可能需要额外的 CPU 时间。如果收集性能确实是问题所在,请对两种类型的收集进行基准测试(见上文)并精确测量以确定两者之间的权衡。我不能替你做那个决定。你说的慢有可能是数据库和应用程序之间的数据传输问题,与集合类型无关。

有关 Scala 集合性能的一般信息,请参阅 Collections: Performance Charateristics

已更新 2

另请注意,任何类型的 Scala 序列通常一次由一个线程按顺序处理(因此得名)。 ListStream 都不适合并行处理它们的元素。如果您需要并行处理一个集合,您将需要一个 parallel 集合类型(scala.collection.parallel 中的集合之一)。 scala.collection.parallel.ParSeq 应该比 ListStream 更快地处理 groupBy,但前提是您有多个 cores/hyperthreads 可用。但是,ParSeq 操作不保证保留分组元素的顺序。