连接列表的有效方法是什么?

时间:2012-07-19 19:58:02

标签: list scala concatenation

当我们有两个列表ab时,如何以有效的方式将这两个列表(顺序不相关)连接到新列表?

如果a ::: ba ++ b有效,我无法从Scala API中找到答案。也许我错过了什么。

1 个答案:

答案 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}}创造)。


换句话说,如果您有关于ListBuffera的相对大小的知识,则应确保前缀为较小中的两个列表。如果您没有这方面的知识,那么您无能为力,因为b上的size操作需要O(N):)


另一方面,在未来的Scala版本中,您可能会看到改进的List级联算法,如this ScalaDays talk中所示。它承诺在O(log N)时间内解决任务。