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)
。 List
s 是严格的,因此在执行另一个操作之前,对列表的任何操作都会一个接一个地处理所有元素。
例如,在 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),这意味着仅在需要时才获取流中的下一个值。它通常被称为 惰性列表。
为了说明 Stream
和 List
之间的区别,让我们重做那个例子:
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 等工具)以确定哪种类型最有效。
顺便说一句,由于 Stream
和 List
都是 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
操作,List
与 Stream
的性能将非常相似。取决于它的使用方式,Stream
可能比 List
需要更少的内存,但可能需要额外的 CPU 时间。如果收集性能确实是问题所在,请对两种类型的收集进行基准测试(见上文)并精确测量以确定两者之间的权衡。我不能替你做那个决定。你说的慢有可能是数据库和应用程序之间的数据传输问题,与集合类型无关。
有关 Scala 集合性能的一般信息,请参阅 Collections: Performance Charateristics。
已更新 2
另请注意,任何类型的 Scala 序列通常一次由一个线程按顺序处理(因此得名)。 List
和 Stream
都不适合并行处理它们的元素。如果您需要并行处理一个集合,您将需要一个 parallel 集合类型(scala.collection.parallel
中的集合之一)。 scala.collection.parallel.ParSeq
应该比 List
或 Stream
更快地处理 groupBy
,但前提是您有多个 cores/hyperthreads 可用。但是,ParSeq
操作不保证保留分组元素的顺序。
我有一个场景,我以对象流的形式获取数据库数据。 在将其转换为一系列 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)
。 List
s 是严格的,因此在执行另一个操作之前,对列表的任何操作都会一个接一个地处理所有元素。
例如,在 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),这意味着仅在需要时才获取流中的下一个值。它通常被称为 惰性列表。
为了说明 Stream
和 List
之间的区别,让我们重做那个例子:
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 等工具)以确定哪种类型最有效。
顺便说一句,由于 Stream
和 List
都是 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
操作,List
与 Stream
的性能将非常相似。取决于它的使用方式,Stream
可能比 List
需要更少的内存,但可能需要额外的 CPU 时间。如果收集性能确实是问题所在,请对两种类型的收集进行基准测试(见上文)并精确测量以确定两者之间的权衡。我不能替你做那个决定。你说的慢有可能是数据库和应用程序之间的数据传输问题,与集合类型无关。
有关 Scala 集合性能的一般信息,请参阅 Collections: Performance Charateristics。
已更新 2
另请注意,任何类型的 Scala 序列通常一次由一个线程按顺序处理(因此得名)。 List
和 Stream
都不适合并行处理它们的元素。如果您需要并行处理一个集合,您将需要一个 parallel 集合类型(scala.collection.parallel
中的集合之一)。 scala.collection.parallel.ParSeq
应该比 List
或 Stream
更快地处理 groupBy
,但前提是您有多个 cores/hyperthreads 可用。但是,ParSeq
操作不保证保留分组元素的顺序。