以前,我正在寻找具有以下特征的数据结构。
在Java中,我使用LinkHashSet,在Python中,我使用OrderedDict
现在,在2个要求的基础上,我希望有一个额外的要求
data[123]
是否有可用的数据结构?或者我需要回退使用List
? List
能够满足第2和第3个要求,但不能满足第1个要求。我可能需要在插入过程中执行手动(和慢速检查),以避免重复?
答案 0 :(得分:3)
Java中的一个简单方法是创建一个包含Set
和List
接口的包装类,它包含HashSet
和ArrayList
。更新操作需要更新两个内部集合,并且读取操作将映射到任何内部集合,以提供正确的语义和最佳性能。唯一有点棘手的方法是iterator()
,您需要安排remove
更新两个集合。
这种方法将为您提供“两全其美”的读取操作性能,但更新速度必然会更慢。特别是,在给定位置插入并删除将是O(N)
操作。
(我注意到LinkedHashSet不是直接解决方案,因为它不提供get(int)
方法。您可以通过LinkedHashSet迭代器实现此方法,这使得它成为O(N)
操作。可能不是你想要的。)
关注
我无法找到实现Set
和List
接口的通用实现类。我认为原因是组合接口时存在语义异常。例如,(如@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那样的键。实现iteritems
和itervalues
或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