如果值是对象并且这些对象的属性是键,是不是有比 Dictionary 更好的数据结构?
Posted
技术标签:
【中文标题】如果值是对象并且这些对象的属性是键,是不是有比 Dictionary 更好的数据结构?【英文标题】:Is there a better data structure than Dictionary if the values are objects and a property of those objects are the keys?如果值是对象并且这些对象的属性是键,是否有比 Dictionary 更好的数据结构? 【发布时间】:2011-01-10 09:35:59 【问题描述】:我有一个Dictionary<int, object>
,其中int
是obj
的一个属性。有没有更好的数据结构呢?我觉得使用属性作为键是多余的。
此Dictionary<int, obj>
是容器类中的一个字段,允许基于int
id 编号随机索引到obj
值。容器类中的简化(无异常处理)索引器如下所示:
obj this[int id]
get return this.myDictionary[id];
其中myDictionary
是前面提到的持有对象的Dictionary<int, obj>
。
这可能是快速随机访问的典型方式,但我想获得第二意见。
【问题讨论】:
Is there a dictionary like collection that can use a property of its value as the key? 的可能重复项 【参考方案1】:如果出厂设置带来的额外开销不值得,您可以轻松实现自己的KeyedCollection
。 System.Collections.ObjectModel
中的原始 KeyedCollection
在内部是 Dictionary<TKey, TItem>
和 List<TItem>
,这意味着您可以在 IList<>
和 IDictionary<>
上定义操作。例如,您可以插入、按索引访问、按插入顺序遍历集合(所有这些都是IList<>
促进的),同时您可以基于键进行快速查找(借助字典)。这意味着当您添加或删除一个项目时,它们必须在两个基础集合上执行,除了用于保存额外 List<>
的小内存开销(但对象不会因此而重复)。虽然加法速度没有太大影响(List<>
加法是 O(1)),removal speed is affected a little.
如果您不关心插入顺序和按索引访问:
public class KeyedCollection<TKey, TItem> : ICollection<TItem>
MemberInfo _keyInfo;
Func<TItem, TKey> _keySelector;
Dictionary<TKey, TItem> _dict;
public TItem this[TKey key]
get return _dict[key];
public int Count
get return _dict.Count;
public bool IsReadOnly
get return false;
public ICollection<TKey> Keys
get return _dict.Keys;
private ICollection<TItem> Items
get return _dict.Values;
public KeyedCollection(Expression<Func<TItem, TKey>> keySelector, IEqualityComparer<TKey> comparer = null)
var keyExpression = keySelector.Body as MemberExpression;
if (keyExpression != null)
_keyInfo = keyExpression.Member;
_keySelector = keySelector.Compile();
_dict = new Dictionary<TKey, TItem>(comparer);
private TKey GetKeyForItem(TItem item)
return _keySelector(item);
public bool ContainsKey(TKey key)
return _dict.ContainsKey(key);
public bool Contains(TItem item)
return ContainsKey(GetKeyForItem(item));
public bool TryGetItem(TKey key, out TItem item)
return _dict.TryGetValue(key, out item);
public void Add(TItem item)
_dict.Add(GetKeyForItem(item), item);
public void AddOrUpdate(TItem item)
_dict[GetKeyForItem(item)] = item;
public bool UpdateKey(TKey oldKey, TKey newKey)
TItem oldItem;
if (_keyInfo == null || !TryGetItem(oldKey, out oldItem) || !SetItem(oldItem, newKey)) // important
return false;
RemoveKey(oldKey);
Add(oldItem);
return true;
private bool SetItem(TItem item, TKey key)
var propertyInfo = _keyInfo as PropertyInfo;
if (propertyInfo != null)
if (!propertyInfo.CanWrite)
return false;
propertyInfo.SetValue(item, key, null);
return true;
var fieldInfo = _keyInfo as FieldInfo;
if (fieldInfo != null)
if (fieldInfo.IsInitOnly)
return false;
fieldInfo.SetValue(item, key);
return true;
return false;
public bool RemoveKey(TKey key)
return _dict.Remove(key);
public bool Remove(TItem item)
return RemoveKey(GetKeyForItem(item));
public void Clear()
_dict.Clear();
public void CopyTo(TItem[] array, int arrayIndex)
Items.CopyTo(array, arrayIndex);
public IEnumerator<TItem> GetEnumerator()
return Items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
我已经实现了ICollection<TItem>
以使其更符合标准 - 而且您还可以获得漂亮的集合初始化器语法! :)
示例用法:
var p1 = new Person Name = "a" ;
var p2 = new Person Name = "b" ;
var people = new KeyedCollection<string, Person>(p => p.Name) p1, p2 ;
// p1 == people["a"];
// p2 == people["b"];
【讨论】:
【参考方案2】:框架中没有执行此操作的具体类。不过有一个抽象的,KeyedCollection。您必须从该类派生您自己的类并实现 GetKeyForItem() 方法。这很简单,只需返回要索引的属性的值即可。
这就是您需要做的所有事情,但请注意 ChangeItemKey()。当您用作键的属性更改值时,您必须做一些有意义的事情。如果您确保该属性是不可变的(只有一个吸气剂),那就很容易了。但是当你不这样做时会很尴尬,对象本身现在需要意识到它被存储在你的集合中。如果您不对其进行任何操作(调用 ChangeItemKey),则该对象会在集合中丢失,您将无法找回它。非常接近泄漏。
注意 Dictionary 如何通过分别指定键值和对象来回避这个问题。您可能仍然无法找回该对象,但至少它不会因设计而丢失。
【讨论】:
很好的建议。正如我向@Lee 提到的那样,这种方法具有暴露一堆其他方法的不良副作用,我不希望我的班级实施。其中最糟糕的是Clear
方法。在这种情况下,我需要将 KeyedCollection
设为只读。
根据您的需要,您可以覆盖例如ClearItems
干脆什么都不做(甚至扔掉)。
@Brian 我认为 Lookup 可能是您想要的,因为我相信您一旦创建就无法修改它。我不确定它是否强制执行密钥的唯一性。【参考方案3】:
C# dynamic properties 的帖子似乎表明使用字典是一种流行的选择。其他帖子建议使用HashTable
Dictionary vs Hashtable
【讨论】:
【参考方案4】:有一个KeyedCollection 类。
编辑:KeyedCollection 可以在内部使用字典,但对于这种特定场景,它比原始字典更清晰的界面,因为您可以直接按值查找。诚然,我觉得它总体上不是很有用。
【讨论】:
很好的建议。但是,如果我让我的容器暗示KeyedCollection<int, obj>
它会暴露大量我不想暴露的成员。特别是Clear
方法。以上是关于如果值是对象并且这些对象的属性是键,是不是有比 Dictionary 更好的数据结构?的主要内容,如果未能解决你的问题,请参考以下文章