【问题标题】:Is there any data structure which can avoid duplication, preserve order and random access有没有可以避免重复、保持顺序和随机访问的数据结构
【发布时间】:2010-11-12 01:42:53
【问题描述】:

之前,我正在寻找具有以下特征的数据结构。

  • 避免重复
  • 迭代顺序与插入顺序相同

在Java中,我使用LinkHashSet,在Python中,我使用OrderedDict

现在,除了 2 个要求之外,我还想要一个额外的要求

  • 能够通过索引随机访问,意味着我可以通过data[123]访问

有没有可用的数据结构?或者我需要退回使用ListList 能够完全满足第 2 和第 3 个要求,但不能满足第 1 个要求。我可能需要在插入过程中执行手动(和慢速检查)以避免重复?

【问题讨论】:

  • 为什么this answer 对您在 python 中准确解决这个主题的问题不起作用?
  • @aaronasterling:他似乎是在要求一个合适的数据结构,而不是关于实现这个的任何特定的现有类(据我所知,Python 的标准库中不存在)。
  • @aaronasterling :我知道它不起作用。我只想知道,市场上任何可用的标准库。
  • @Yan Cheng CHEOK 我没有删除我对你上一个问题的回答。它有一个你在 Python 中寻找的实现。我不认为有一个预先存在的命名数据结构来做你想做的事。
  • 所以你想让它和data.items()[123]做同样的事情?

标签: java python collections


【解决方案1】:

Java 中的一个简单方法是创建一个实现SetList 接口的包装类,并且包含HashSetArrayList。更新操作需要更新两个内部集合,并且读取操作将映射到提供正确语义和最佳性能的任何内部集合。唯一有点棘手的方法是iterator(),您需要安排remove 更新两个集合。

这种方法将为您提供“两全其美”的读取操作性能,但更新速度必然会变慢。特别是,在给定位置插入和删除将是O(N) 操作。

(我注意到 LinkedHashSet 不是直接的解决方案,因为它不提供 get(int) 方法。您可以通过 LinkedHashSet 迭代器实现此方法,从而使其成为 O(N) 操作。可能不是您想要。)

跟进

我找不到一个通用的实现类来实现SetList 接口。我认为原因是当您组合接口时存在语义异常。例如,(正如@ColinD 所指出的)如果您调用E set(int, E) 并带有一个已经在列表中的元素,则不清楚结果应该是什么。以一种让每个人都满意的方式来处理这个问题可能是不可能的,我可以理解他们为什么可能决定不去防水油坑游泳。

但是,如果您正在为应用程序的内部使用创建Set + List 类,我不认为这是一个主要问题。你要么

  • 选择适合您的应用程序的语义,
  • 将您的应用程序编码为根本不使用该方法,或者
  • 对您的应用程序进行编码以避免被异常影响。

(例如,您可以将其编码为忽略set 方法的结果,如果存在重复则抛出未经检查的异常,或者如果存在重复则返回null 或某个可区分的对象。)

郑重声明,自定义集合类违反接口约定并非不可原谅。事实上,即使是 Java 设计者也这样做了——参见 IdentityHashMap。不可原谅的是没有在 javadocs 中记录违反合同的行为。

【讨论】:

  • @Stephen C:有可用的标准库吗?我不想自己创建。
  • 我不知道,但您可以查看常见的地方:Apache Commons Collections 和 Google Collections。问题是您的要求很难以有效的方式满足。大多数人重新设计他们的算法以避免这种特殊的组合。
  • 我不确定我是否会为这样的数据结构实现 ListSet,因为它与每个数据结构的预期都有些不同。
  • @ColinD - List 规范说:“与集合不同,列表通常允许重复元素。”。 IMO,这意味着对于特殊目的List 不允许重复是可以的。
  • 您不能在同一个类中同时实现 List 和 Set,因为它们对多个方法(add、equals、hashCode...)的合同要求相互冲突
【解决方案2】:

如果您可以使用不可变集合,请使用来自 Guava 的 ImmutableSet,它具有 asList() 视图来提供索引访问。

【讨论】:

    【解决方案3】:

    java.util.Set 不提供像 get() 和 set() 这样的随机访问方法,因此它的大多数/所有实现也不提供。您可以创建自己的 Set 实现来提供此功能,可能使用 ArrayList 来保存数据。

    【讨论】:

      【解决方案4】:

      LinkedHashSet 类提供了 toArray-Method,它应该可以满足您的需求。

      【讨论】:

        【解决方案5】:

        您不会找到执行此操作的基本数据结构;您正在寻找的目标排除了所有这些目标。您可能会发现一个更深奥的方法可以做到这一点,但最简单的方法是使用复合数据结构,并行维护两个数据结构。

        事实上,这就是collections.OrderedDict 在幕后所做的。但是,这不是您想要的:由于它不是为支持索引而设计的,因此它在底层使用链表来保留顺序。链表不能进行索引——缺少缓慢的线性扫描,您通常希望避免这种情况,因为如果在循环中使用它往往会导致 O(n^2) 对您不利。

        这是一个简单的实现。它维护两个数据结构:一个列表,在设置时保留项目的顺序,以及一个字典,用于按键快速查找。两者都保存值,并且都保存对方的键:字典保存列表中的索引,列表保存字典中的键。这使得相互引用每个数据结构变得容易,因此它可以有效地处理赋值和迭代。

        请注意,这并没有实现所有操作,仅实现基本操作:dict-style assignment a['x'] = 1、dict-style lookup a['x']、list-style assignment a.set_value_by_index(0, 1) 和 list-style lookup a.get_value_by_index(0)

        另请注意:这不会对 dict 样式和 list 样式的操作使用相同的语法。这是令人困惑和邪恶的,迟早会严重咬你。这不会将a[0] 变成列表样式的查找;如果这是您想要的,请明确并使用get_value_by_index。不要魔法,试着根据参数类型来猜测。

        最后,它提供了简单的 dict 风格的迭代,像 dict 一样产生键。实现 iteritemsitervalues 或 Python3 视图之类的东西是明显的扩展。

        class IndexableUniqueList(object):
            """
            >>> a = IndexableUniqueList()
            >>> a['x'] = 1
            >>> a['x']
            1
            >>> a['y'] = 2
            >>> a['y']
            2
            >>> a.get_key_by_index(0)
            'x'
            >>> a.get_value_by_index(0)
            1
            >>> a.get_key_by_index(1)
            'y'
            >>> a.get_value_by_index(1)
            2
            >>> a['x'] = 3
            >>> a.get_key_by_index(0)
            'x'
            >>> a.get_value_by_index(0)
            3
            >>> a.set_value_by_index(0, 4)
            >>> a['x']
            4
            >>> [val for val in a]
            ['x', 'y']
            """
            def __init__(self):
                self.items_by_index = []
                self.items_by_key = {}
        
            def __getitem__(self, key):
                return self.items_by_key[key][1]
        
            def __setitem__(self, key, value):
                if key in self.items_by_key:
                    idx, old_value = self.items_by_key[key]
                    self.items_by_key[key] = (idx, value)
                    self.items_by_index[idx] = (key, value)
                    return
        
                idx = len(self.items_by_index)
                self.items_by_key[key] = (idx, value)
                self.items_by_index.append((key, value))
            def get_key_by_index(self, idx):
                return self.items_by_index[idx][0]
            def get_value_by_index(self, idx):
                key = self.get_key_by_index(idx)
                return self.items_by_key[key][1]
            def set_value_by_index(self, idx, value):
                key = self.items_by_index[idx][0]
                self[key] = value
            def __iter__(self):
                for key, value in self.items_by_index:
                    yield key
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-23
          • 2017-07-10
          相关资源
          最近更新 更多