通用键/值对集合保留了插入顺序?

我正在寻找像Dictionary 这样的东西,但保证它保留了插入顺序。 由于Dictionary是一个哈希表,我不认为它。

是否有一个通用集合,或者我是否需要使用旧的.NET 1.1集合之一?

那没有。 但是, System.Collections.Specialized.OrderedDictionary应该可以解决大部分需求。

编辑:另一种选择是将其变成通用。 我没有测试它,但它编译(C#6),应该工作。 但是,它仍将具有与Ondrej Petrzilka在下面的评论中提到的相同的限制。

public class OrderdDictionary { public OrderedDictionary UnderlyingCollection { get; } = new OrderedDictionary(); public K this[T key] { get { return (K)UnderlyingCollection[key]; } set { UnderlyingCollection[key] = value; } } public K this[int index] { get { return (K)UnderlyingCollection[index]; } set { UnderlyingCollection[index] = value; } } public ICollection Keys => UnderlyingCollection.Keys.OfType().ToList(); public ICollection Values => UnderlyingCollection.Values.OfType().ToList(); public bool IsReadOnly => UnderlyingCollection.IsReadOnly; public int Count => UnderlyingCollection.Count; public IDictionaryEnumerator GetEnumerator() => UnderlyingCollection.GetEnumerator(); public void Insert(int index, T key, K value) => UnderlyingCollection.Insert(index, key, value); public void RemoveAt(int index) => UnderlyingCollection.RemoveAt(index); public bool Contains(T key) => UnderlyingCollection.Contains(key); public void Add(T key, K value) => UnderlyingCollection.Add(key, value); public void Clear() => UnderlyingCollection.Clear(); public void Remove(T key) => UnderlyingCollection.Remove(key); public void CopyTo(Array array, int index) => UnderlyingCollection.CopyTo(array, index); } 

实际上有一个,它是通用的,自.net 2.0以来一直存在。 它被称为KeyedCollection但是,它具有从值中构造键的限制 ,因此它不是通用的键/值对集合。

如果你需要它作为IDictionary ,它有一个.Dictionary属性。

我遇到的一个小问题是它是一个抽象类,你必须将它子类化并实现:

 protected abstract TKey GetKeyForItem(TItem item) 

为了这个目的,我宁愿只将lambda传递给构造函数,但是再说一遍,我猜一个虚方法比lambda快一点(对此有任何评论)。

有一个OrderedDictionary类是一个字典,但可以按插入顺序编入索引,但它不是一般化的。 目前在.Net框架中没有一个普遍的。

我已经从.Net团队中的某个人那里读过一条评论说他们可能会在将来实现一个通用的版本,但如果是这样的话,它很可能被称为IndexableDictionary而不是OrderedDictionary以使其行为更加明显。

编辑:找到了报价。 它出现在OrderedDictionary的MSDN页面上,归功于微软的David M. Kean:

这种类型实际上是错误的; 它不是一个“有序”字典,而是一个“索引”字典。 虽然,今天没有这种类型的等效通用版本,如果我们将来添加一个,我们可能会命名为’IndexedDictionary’类型。

好吧,您可以使用List> ,这将保留订单…但是您将失去字典的查找function。 为什么需要保留订单?

代码项目有一个通用的实现,它带有合理数量的测试用例。

作者选择了一个相当有趣的名字(KeyedList),这使得它很难找到。

是非generics Systems.Collections.Specialized.OrderedDictionary类型的包装器。

此类型将按插入顺序返回键/值/对序列,非常类似于Ruby 2.0哈希。

它不需要C#6魔术,符合IDictionary (这也意味着访问未分配的键会引发exception),并且应该是可序列化的。

每个关于Adrian答案的注释都给它命名为“IndexedDictionary”。

因人而异。

 using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; ///  /// A dictionary that maintains insertion ordering of keys. /// /// This is useful for emitting JSON where it is preferable to keep the key ordering /// for various human-friendlier reasons. /// /// There is no support to manually re-order keys or to access keys /// by index without using Keys/Values or the Enumerator (eg). ///  [Serializable] public sealed class IndexedDictionary : IDictionary { // Non-generic version only in .NET 4.5 private readonly OrderedDictionary _backing = new OrderedDictionary(); private IEnumerable> KeyValuePairs { get { return _backing.OfType() .Select(e => new KeyValuePair((TKey)e.Key, (TValue)e.Value)); } } public IEnumerator> GetEnumerator() { return KeyValuePairs.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(KeyValuePair item) { _backing[item.Key] = item.Value; } public void Clear() { _backing.Clear(); } public bool Contains(KeyValuePair item) { return _backing.Contains(item.Key); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { KeyValuePairs.ToList().CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair item) { TValue value; if (TryGetValue(item.Key, out value) && Equals(value, item.Value)) { Remove(item.Key); return true; } return false; } public int Count { get { return _backing.Count; } } public bool IsReadOnly { get { return _backing.IsReadOnly; } } public bool ContainsKey(TKey key) { return _backing.Contains(key); } public void Add(TKey key, TValue value) { _backing.Add(key, value); } public bool Remove(TKey key) { var result = _backing.Contains(key); if (result) { _backing.Remove(key); } return result; } public bool TryGetValue(TKey key, out TValue value) { object foundValue; if ((foundValue = _backing[key]) != null || _backing.Contains(key)) { // Either found with a non-null value, or contained value is null. value = (TValue)foundValue; return true; } value = default(TValue); return false; } public TValue this[TKey key] { get { TValue value; if (TryGetValue(key, out value)) return value; throw new KeyNotFoundException(); } set { _backing[key] = value; } } public ICollection Keys { get { return _backing.Keys.OfType().ToList(); } } public ICollection Values { get { return _backing.Values.OfType().ToList(); } } } 

我知道你正在编写C#,但Java有一个名为LinkedHashMap的类,它使用私有的LinkedList来维护键的插入顺序。 如果您找不到合适的通用解决方案,那么这可能是您实施自己的解决方案的开始。

保留插入的通用键/值对的另一个选项是使用如下内容:

 Queue> 

这将是一个有保证的有序列表。 您可以在类似于添加/删除字典的有序派系中对队列进行排队和出列,而不是调整arrays的大小。 它通常可以作为非resize的有序(通过插入)数组和自动调整无序(通过插入)列表之间的中间地带。

如果您需要不断复杂的AddRemoveContainsKey和订单保留,那么.NET Framework 4.5中就没有这样的通用。

如果您对第三方代码没问题,请查看我的存储库(许可的MIT许可证): https : //github.com/OndrejPetrzilka/Rock.Collections

OrderedDictionary集合:

  • 基于经典Dictionary源代码(来自.NET Core)
  • 保留插入顺序并允许手动重新排序
  • function颠倒了枚举
  • 具有 Dictionary 相同的操作复杂度
  • Dictionary相比, AddRemove操作慢约20%
  • 每个项目消耗8个字节的内存

码:

 //A SortedDictionary is sorted on the key (not value) System.Collections.Generic.SortedDictionary testSortDic = new SortedDictionary(); //Add some values with the keys out of order testSortDic.Add("key5", "value 1"); testSortDic.Add("key3", "value 2"); testSortDic.Add("key2", "value 3"); testSortDic.Add("key4", "value 4"); testSortDic.Add("key1", "value 5"); //Display the elements. foreach (KeyValuePair kvp in testSortDic) { Console.WriteLine("Key = {0}, value = {1}", kvp.Key, kvp.Value); } 

输出:

 Key = key1, value = value 5 Key = key2, value = value 3 Key = key3, value = value 2 Key = key4, value = value 4 Key = key5, value = value 1