Scala-Seq与列表性能

Scala-Seq与列表性能,scala,jvm,scala-collections,Scala,Jvm,Scala Collections,所以我有点困惑。我有一段Scala代码(确切的代码并不那么重要)。我把我所有的方法都写下来取Seq[T]。这些方法大多是尾部递归的,并使用Seq[T]作为累加器,最初像Seq()一样输入。有趣的是,当我用具体的List()实现交换所有签名时,我发现性能提高了三倍 Seq的默认实现实际上是一个不可变的列表,这不是吗?如果是这样的话,到底发生了什么 调用Seq(1,2,3)和调用List(1,2,3)都会导致1::2::3::Nil。该方法只是一种非常通用的方法,如下所示: def apply[A]

所以我有点困惑。我有一段Scala代码(确切的代码并不那么重要)。我把我所有的方法都写下来取Seq[T]。这些方法大多是尾部递归的,并使用Seq[T]作为累加器,最初像Seq()一样输入。有趣的是,当我用具体的List()实现交换所有签名时,我发现性能提高了三倍

Seq的默认实现实际上是一个不可变的列表,这不是吗?如果是这样的话,到底发生了什么

调用
Seq(1,2,3)
和调用
List(1,2,3)
都会导致
1::2::3::Nil
。该方法只是一种非常通用的方法,如下所示:

def apply[A](elems: A*): CC[A] = {
  if (elems.isEmpty) empty[A]
  else {
    val b = newBuilder[A]
    b ++= elems
    b.result()
  }
}
def result: List[A] = toList

/** Converts this buffer to a list. Takes constant time. The buffer is
 *  copied lazily, the first time it is mutated.
 */
override def toList: List[A] = {
  exported = !isEmpty
  start
}
newBuilder
是这里最重要的东西。与会代表:

因此
Seq
Builder
是一个
可变的.ListBuffer
Seq
通过将元素附加到空的
ListBuffer
然后调用它来构造,其实现方式如下:

def apply[A](elems: A*): CC[A] = {
  if (elems.isEmpty) empty[A]
  else {
    val b = newBuilder[A]
    b ++= elems
    b.result()
  }
}
def result: List[A] = toList

/** Converts this buffer to a list. Takes constant time. The buffer is
 *  copied lazily, the first time it is mutated.
 */
override def toList: List[A] = {
  exported = !isEmpty
  start
}
List
还有一个
ListBuffer
作为
Builder
。它经历了一个稍微不同但相似的建造过程。无论如何,这不会有很大的区别,因为我假设您的大多数算法都是将内容预先添加到
Seq
,而不是一直调用
Seq.apply(…)
。即使你做到了,也不会有多大区别


如果看不到具有该行为的代码,就很难说出导致您看到的行为的原因。

您是否有一些代码可以证明这一点?
Seq
默认实现实际上是一个
ArrayBuffer
,而不是一个
列表
。您拥有的用例实际上是为
List
s定制的,因为它们是尾部递归实现的。这会导致如此巨大的性能差异吗?默认
Seq
的默认实现是
List
。您可以展示代码以及如何调用它吗?根据您的假设,Seq.apply只是一个列表,并且没有太大区别,您实际上可以这样做,对不对:val sequence:LinearSeq[]=Seq(),这应该相应地起作用,因为Seq是一个列表,而List使用LinearSeq特性。但是它不起作用,因此,它关系到如何构造序列,特别是当您需要有效的尾部访问或尾部递归操作(即,对序列执行迭代的方法)时
Seq()。这就是为什么不能将其分配给
LinearSeq
类型的变量。评估
Seq(1).getClass
并查看您得到了什么。但要清楚,我确实同意,如果您想要
列表的性能特征,最好只使用
List
。我刚刚与调试器进行了检查。我假设Seq只使用ListBuffer构建序列,而不处理序列,并使用Traversable中优化程度较低的操作,等等,但事实证明它实际上是从列表及其特征中访问优化的方法。就所有意图和目的而言,您完全正确地声明List.apply可替换为Seq.apply。您应该始终使用三种Seq特征的apply方法。没有任何性能偏差的解释。