Scala Stream vs Scala List vs Scala Sequence有啥区别

Posted

技术标签:

【中文标题】Scala Stream vs Scala List vs Scala Sequence有啥区别【英文标题】:what is the difference between Scala Stream vs Scala List vs Scala SequenceScala Stream vs Scala List vs Scala Sequence有什么区别 【发布时间】:2018-05-23 10:00:50 【问题描述】:

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

【问题讨论】:

【参考方案1】:

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

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

Scala 列表 (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 流 (scala.collection.immutable.Stream) 也是Seq 的子类,但它是惰性(或非严格 ),这意味着仅在需要时才获取流中的下一个值。它通常被称为惰性列表

为了说明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 的序列的代码。这样,您可以提供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)

更新

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

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

更新 2

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

【讨论】:

如果我们需要使用 groupby Func、List 或 Stream,哪个更好? @BharathKumar 我已经更新了我的答案来解决你的问题。是什么让您认为是集合类型导致您的应用程序变慢? 谢谢 Mike。当然会的。 我所做的唯一更改是通过在 DB 中添加一些列来收集类型。除此之外一切都是一样的。你能帮我用任何 IntelliJ 插件来监控 Scala 应用程序的性能以及如何使用它吗?

以上是关于Scala Stream vs Scala List vs Scala Sequence有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

scala中Stream理解

Scala lazy的应用,流Stream和视图View

如何在 Scala 中使用 Stream.cons 编写不泄漏的尾递归函数?

Scala 列表连接,::: vs ++

vs code 安装Scala

Scala:流不懒惰?