为什么Scala集合中没有不可变的双链表



看这个问题,提问者对List中某个元素的第一个和最后一个实例感兴趣,似乎更有效的解决方案是使用可以从列表末尾向后搜索的DoubleLinkedList。然而,在集合API中只有一种实现,而且它是可变的。

为什么没有不可变版本?

因为每次要更改时都必须复制整个列表。使用普通的链表,您至少可以在列表前添加内容,而不必复制所有内容。如果你想复制每一个改动的所有内容,你不需要一个链表。你可以直接使用不可变数组

这种结构有许多障碍,但有一个非常紧迫:双链表不能持久。

背后的逻辑非常简单:从列表中的任何节点,您都可以到达任何其他节点。如果我添加了一个元素X这个列表DL,并试图使用DL的一部分,我面对这种矛盾:从节点指向X可以达到每一个元素(DL)的一部分,但是,通过双向链表的属性,这意味着任何元素的一部分(DL)我可以到达节点指向X以来(DL)应该是不变的一部分,DL的一部分,由于DL不包括节点指向X,不能。

非持久不可变数据结构可能有一些用途,但它们通常对大多数操作不利,因为每当产生派生时都需要重新创建它们。

现在,有一个创建相互引用严格对象的小问题,但这是可以克服的。可以使用按名参数和延迟值,或者可以像Scala的List那样:实际创建一个可变集合,然后将其"冻结"在不可变状态(参见ListBuffer和它的toList方法)。

因为创建具有严格不可变性的相互(循环)引用数据结构在逻辑上是不可能的。

由于存在顺序优先级的原因,不能创建两个彼此指向对方的节点,因为在创建另一个节点时,至少有一个节点不存在。

有可能通过涉及惰性的技巧获得这种循环(这是通过突变实现的),但真正的问题是为什么你首先想要这个东西?

正如其他人所注意到的,没有双链表的持久实现。你需要某种树来接近你想要的特征。

特别是,您可能想要查看手指树,它提供O(1)次对前后的访问,平摊O(1)次对前后的插入,以及O(log n)次在其他地方的插入。(这与大多数其他常用的树形成对比,它们在任何地方都有O(log n)的访问和插入。)

参见:

  • 手指树的视频解释(由clojure.contrib中的手指树实现者提供)
  • 手指树在Scala中的实现(我个人没有使用过,但它是google最热门的)

作为@KimStebel回答的补充,我想补充:
如果你正在寻找适合这个问题的数据结构,那么你可以看看@DanielSpiewak的《极致聪明:Scala中的函数式数据结构》。

相关内容

  • 没有找到相关文章

最新更新