Scala流、Scala列表和Scala序列有什么区别?

5

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

1个回答

20
快速回答:一个Scala流(Scala stream)已经是一个Scala序列(Scala sequence),不需要进行任何转换。以下是进一步的解释...
Scala序列(scala.collection.Seq)是指任何按特定顺序存储元素序列的集合(顺序是任意的,但元素顺序在定义后不会改变)。
Scala列表(scala.collection.immutable.List)是Seq的子类,也是scala.collection.Seq的默认实现。也就是说,Seq(1, 2, 3)被实现为List(1, 2, 3)。列表是严格的,因此在处理所有元素之后才能执行另一个操作。
例如,在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),这意味着只有在需要时才会获取流的下一个值。它通常被称为 lazy list

为了说明 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操作仍然返回一个新的流(Stream(2, 4, 6)),但值只在需要时被获取。
在任何特定情况下,Stream是否比List表现更好取决于您要做什么。如果性能是您的主要目标,建议您使用工具(如ScalaMeter)对代码进行基准测试,以确定哪种类型效果最佳。
顺便说一句,由于StreamList都是Seq的子类,通常的做法是编写需要序列的代码来利用Seq。这样,您可以提供一个ListStream或任何其他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集合性能的一般信息,请参阅集合:性能特性

更新2

还要注意,任何类型的Scala序列通常都会被单个线程按顺序(因此名称)处理,一个接一个。 ListStream都不适合并行处理它们的元素。如果需要并行处理集合,则需要一种parallel集合类型(scala.collection.parallel中的一个集合)。 scala.collection.parallel.ParSeq应该比ListStream更快地处理groupBy,但前提是您有多个核心/超线程可用。但是,ParSeq操作不能保证保留分组元素的顺序。


如果我们需要使用 groupby 函数,List 还是 Stream 哪个更好? - Bharath Kumar
@BharathKumar,我更新了我的答案来回答你的问题。你觉得是集合类型导致你的应用程序变慢了吗? - Mike Allen
谢谢Mike。当然会的。 - Bharath Kumar
我所做的唯一更改是通过在数据库中添加一些列来更改集合类型。除此之外,一切都是相同的。你能帮我找一个IntelliJ插件来监控Scala应用程序的性能以及如何使用它吗? - Bharath Kumar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接