为什么追加到列表不好?

时间:2009-08-24 01:49:56

标签: performance scala functional-programming

我最近开始学习scala,而且我遇到了::(cons)函数,该函数在列表前面。
在“Scala编程”一书中,它指出没有追加函数,因为附加到列表具有性能o(n)而前置函数的性能为o(1)

有些事情让我觉得这句话错了。

性能是否取决于实施?是不是可以简单地用前向和后向链接实现列表并将第一个和最后一个元素存储在容器中?

我想的第二个问题是当我有一个列表时我应该做的事情,例如1,2,3我想在它的末尾加上4?

5 个答案:

答案 0 :(得分:73)

关键是x :: somelist不会改变somelist,而是创建一个新列表,其中包含x后跟somelist的所有元素。这可以在O(1)时间内完成,因为您只需要在新创建的单链表中将somelist设置为x的后继。

如果使用双重链接列表,则x也必须设置为somelist头部的前身,这将修改somelist。因此,如果我们希望能够在O(1)中执行::而不修改原始列表,我们只能使用单链表。

关于第二个问题:您可以使用:::将单个元素列表连接到列表的末尾。这是O(n)操作。

List(1,2,3) ::: List(4)

答案 1 :(得分:16)

其他答案对这一现象给出了很好的解释。如果要将多个项目附加到子例程中的列表中,或者如果要通过附加元素创建列表,则功能习惯用法是以相反的顺序构建列表,同时包含前面的项目,然后在最后反转它。这样就可以得到O(n)性能,而不是O(n²)。

答案 2 :(得分:12)

由于问题刚刚更新,值得注意的是,这里的情况发生了变化。

在今天的Scala中,您只需使用xs :+ x在任何顺序集合的末尾追加项目。 (也有x +: xs前置。许多Scala 2.8+收集操作的助记符是 col on旁边的 col 。)< / p>

这将是O( n ),其默认链接实现为ListSeq,但如果您使用VectorIndexedSeq ,这将是有效的恒定时间。 Scala的Vector可能是Scala最有用的类似列表的集合 - 与Java Vector不同,这些日子几乎没用。

如果您使用的是Scala 2.8或更高版本,则必须阅读collections introduction

答案 3 :(得分:11)

前置更快,因为它只需要两个操作:

  1. 创建新列表节点
  2. 让新节点指向现有列表
  3. 追加需要更多操作,因为你必须遍历到列表的末尾,因为你只有指向头部的指针。

    我以前从未在Scala中编程,但您可以尝试List Buffer

答案 4 :(得分:4)

大多数函数式语言突出显示单链表数据结构,因为它是一种方便的不可变集合类型。当你用函数式语言说“list”时,这通常是你的意思(单链表,通常是不可变的)。对于这种类型,append是O(n)而cons是O(1)。