当我们有两个列表a
和b
时,如何以有效的方式将这两个列表(顺序不相关)连接到新列表?
如果a ::: b
和a ++ b
有效,我无法从Scala API中找到答案。也许我错过了什么。
答案 0 :(得分:8)
在Scala 2.9中,:::
(前置列表)的代码如下:
def :::[B >: A](prefix: List[B]): List[B] =
if (isEmpty) prefix
else (new ListBuffer[B] ++= prefix).prependToList(this)
而++
更通用,因为它需要CanBuildFrom
参数,即它可以返回与List
不同的集合类型:
override def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That = {
val b = bf(this)
if (b.isInstanceOf[ListBuffer[_]]) (this ::: that.seq.toList).asInstanceOf[That]
else super.++(that)
}
因此,如果您的返回类型为List
,则两者执行相同的操作。
ListBuffer
是一种聪明的机制,因为它可以用作变异构建器,但最终会被toList
方法“消耗”。那么(new ListBuffer[B] ++= prefix).prependToList(this)
所做的是,首先按prefix
(在示例a
中)添加所有元素,取O(| a |)时间。然后它调用prependToList
,这是常量时间操作(接收器或b
,不需要拆开)。因此,总时间为O(| a |)。
另一方面,正如pst指出的那样,我们有reverse_:::
:
def reverse_:::[B >: A](prefix: List[B]): List[B] = {
var these: List[B] = this
var pres = prefix
while (!pres.isEmpty) {
these = pres.head :: these
pres = pres.tail
}
these
}
因此使用a reverse_::: b
,这又需要O(| a |),因此与其他两个方法相比,效率更高或更低(尽管对于小的列表大小,您可以节省具有中间{{}的开销。 1}}创造)。
换句话说,如果您有关于ListBuffer
和a
的相对大小的知识,则应确保前缀为较小中的两个列表。如果您没有这方面的知识,那么您无能为力,因为b
上的size
操作需要O(N):)
另一方面,在未来的Scala版本中,您可能会看到改进的List
级联算法,如this ScalaDays talk中所示。它承诺在O(log N)时间内解决任务。