是否有任何数据结构可以避免重复,保留顺序和随机访问

时间:2010-11-12 01:42:53

标签: java python collections

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

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

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

现在,在2个要求的基础上,我希望有一个额外的要求

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

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

5 个答案:

答案 0 :(得分:3)

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。不可原谅的是不记录javadoc中的合同违规行为。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

java.util.Set不提供get()和set()等随机访问方法,因此大部分/全部实现都不提供。您可以创建自己的Set实现来提供此功能,也可以使用ArrayList来保存数据。

答案 3 :(得分:0)

LinkedHashSet类提供toArray-Method,它应该符合您的需要。

答案 4 :(得分:0)

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

事实上,这就是collections.OrderedDict所做的事情。但这并不是你想要的:因为它不是为了支持索引而设计的,所以它使用引擎盖下的链表来保存订单。链接列表无法进行索引 - 缺少慢速线性扫描,您通常要避免这种情况,因为如果在循环中使用它,它往往会转向O(n ^ 2)。

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

请注意,这不会实现每个操作,只是基本操作:dict-style assignment a['x'] = 1,dict-style lookup a['x'],list-style assignment a.set_value_by_index(0, 1)和list-样式查找a.get_value_by_index(0)

另请注意:对于dict-style和list-style操作,这不使用相同的语法。那令人困惑和邪恶,迟早会严重咬你。这不会将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