【问题标题】:How to implement immutable collection with constant append and random access time?如何实现具有恒定附加和随机访问时间的不可变集合?
【发布时间】:2014-07-08 12:36:30
【问题描述】:

我正在寻找像 this one proposed by Eric Lippert 这样的解决方案。这是一个很好的实现,因为它是不可变的,加上附加时间是O(1),但它的缺点是O(i) 随机访问时间。

在另一边there is a great implementation of collectionO(1) 在追加和随机访问。唯一的问题是它强烈依赖于可变性。

我的问题是如何实现一个集合了这两种解决方案的优点的集合?那就是:

  1. 不变性
  2. O(1)追加时间
  3. O(1)随机访问时间

内存复杂度对我来说不是什么大问题。

【问题讨论】:

  • 试用 C5 泛型:itu.dk/research/c5
  • 具体是哪一个?
  • “应该足够了”?我不这么认为[来自文档]:“结构修改,例如添加、插入、更新或删除项目将引发 ReadOnlyCollectionException。”。它不提供附加功能。
  • @MaciejSz Scala documentation 声称其不可变 HashMap 的添加/查找复杂性“实际上是恒定的”。可能值得看看它是如何工作的。虽然它不提供随机访问,但您可以将这样的映射包装在 Eric 之类的列表实现中,并将其用作基于索引的查找。
  • @PeterLawrey 当您将集合聚合到另一个不可变对象中然后想要对该对象执行一些操作时,这会成为一个问题。如果此操作产生了该对象的新实例,其中修改了聚合集合的内容,那么别无选择,只能使集合不可变,或者破坏整个不可变链。另一种选择是克隆整个集合,这(用 Eric 的话来说)当然是愚蠢的。

标签: java c# data-structures collections time-complexity


【解决方案1】:

我不知道如何实现一个满足您所有要求的列表——不变性、持久性、O(1) 插入、O(1) 删除、O(1) 随机访问。

我给你的建议是(1)如果你对这个话题感兴趣,请阅读 Chris Okasaki 的书。 (或者,获取他的论文的副本,这是本书的基础。)并且 (2) Chris Okasaki 为您的目的建议了此处描述的数据结构:

http://www.codeproject.com/Articles/9680/Persistent-Data-Structures#RandomAccessLists

这个列表是 O(1) 插入和 O(1) 删除到头部和 O(lg) 用于随机访问。

【讨论】:

    【解决方案2】:

    我不确定你将如何获得 O(1) 附加和 O(1) 随机访问,除非你包含另一个数据结构。

    通常,如果您希望能够追加元素,您可以复制源集合,它保留O(1) 随机访问但给您O(n) 追加;或者你可以做 Eric 做的事情,并保留旧的列表段,这给你 O(1) 附加时间但 O(n) 随机访问。假设恒定附加时间很关键,那么您可以选择合并第二个数据结构来提供恒定时间随机访问。

    Scala documentation 声称其不可变 HashMap 的“有效恒定”添加和查找时间。如果属实,我建议看看他们的实施。您可以采用像 Eric 的解决方案,并为元素本身添加一个有效的不可变索引映射。不过,这会增加一些内存开销,虽然追加操作会很有效,但插入不会。

    然而,我对 Scala HashMap 的性能声明有点怀疑。 Other immutable map 实现声称 log32(n) 复杂性,这可能适用于添加和查找操作。我的直觉告诉我,尽管log32(n) 非常合理,但你不会比对数复杂度更好。

    【讨论】:

    • 采用像 Eric 的解决方案并添加有效的不可变列表索引:这是我在提出这个问题之前一直在研究的。问题是 列表索引的高效不可变 实际上是我需要的相同数据结构。你能看到这里的无限递归吗? :)
    • @MaciejSz 啊,那应该是“索引的map”。而且,是的,问题几乎相同,但考虑到您的性能要求,寻找不可变 map 结构可能比不可变 list 结构更幸运。
    【解决方案3】:
            var bag = new HashBag<int>
                      {
                          1,
                          2,
                          3
                      };
            var g = new GuardedCollection<int>(bag);
            bag.Add(4);
            g.Add(5);
    

    HashBag 仍然是可变的,但您仍然可以将它作为不可变的 GuardedCollection 传递给另一个消费者。

    【讨论】:

    • 1) 在真正的不可变集合中(在我看来),你不需要记住底层集合)——但是好的,我们可以创建自己的包装器。 2)向HashBag线程中添加元素安全吗?
    • 好的,但是如果消费者希望附加另一个项目,现在会发生什么?他必须从g:GuardedCollection(即O(n))中提取底层元素。我提到的链表实现是免费的。
    • @MaciejSz 我不确定我是否满足您的要求。您想要一个不可变的集合并且希望能够附加到它?这似乎是一个矛盾。
    • @Darek - 不可变集合是线程安全的,所以如果需要这样的集合,我认为其他解决方案也应该满足该要求。
    • @Darek 集合是不可变的;追加产生一个新集合,就像追加两个字符串产生一个新字符串一样。在这两种情况下,原始文件都是不可变的,并且保持不变。
    猜你喜欢
    • 1970-01-01
    • 2013-09-13
    • 1970-01-01
    • 2019-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多