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

时间:2009-09-08 22:42:42

标签: c# .net data-structures

我正在寻找像词典< K,V>但保证它保留了订单。由于Dictionary是一个哈希表,我不认为它。

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

10 个答案:

答案 0 :(得分:24)

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

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

    public class OrderdDictionary<T, K>
    {
        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<T> Keys => UnderlyingCollection.Keys.OfType<T>().ToList();
        public ICollection<K> Values => UnderlyingCollection.Values.OfType<K>().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);
    }

答案 1 :(得分:13)

实际上有一个,它是通用的,自.net 2.0以来一直存在。它被称为KeyedCollection<TKey, TItem>但是,它带来了从值构造键的限制,因此它不是通用的键/值对集合。 (虽然您当然可以像KeyedCollection<TKey, Tuple<TKey, TItem>>一样使用它作为解决方法)。

如果您需要IDictionary<TKey, TItem>,则它具有.Dictionary属性。

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

protected abstract TKey GetKeyForItem(TItem item)

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

修改正如评论中提出的问题:KeyedCollection保留了订单,因为它继承自Collection<T>,它来自IList<T>。另请参阅Add方法的文档:将对象添加到Collection的末尾。)。

答案 2 :(得分:8)

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

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

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

  

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

答案 3 :(得分:5)

好吧,您可以使用List<KeyValuePair<K,V>>,这将保留订单...但是您将失去字典的查找功能。为什么需要保留订单?

答案 4 :(得分:3)

code project上有一个通用的实现,它带有合理数量的测试用例。

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

答案 5 :(得分:3)

以下是非通用 IDictionary<TKey,TValue>类型的包装。

此类型将以插入顺序返回键/值/对序列,就像Ruby 2.0哈希一样。

它不需要C#6魔法,符合using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; /// <summary> /// 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). /// </summary> [Serializable] public sealed class IndexedDictionary<TKey, TValue> : IDictionary<TKey, TValue> { // Non-generic version only in .NET 4.5 private readonly OrderedDictionary _backing = new OrderedDictionary(); private IEnumerable<KeyValuePair<TKey, TValue>> KeyValuePairs { get { return _backing.OfType<DictionaryEntry>() .Select(e => new KeyValuePair<TKey, TValue>((TKey)e.Key, (TValue)e.Value)); } } public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return KeyValuePairs.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(KeyValuePair<TKey, TValue> item) { _backing[item.Key] = item.Value; } public void Clear() { _backing.Clear(); } public bool Contains(KeyValuePair<TKey, TValue> item) { return _backing.Contains(item.Key); } public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { KeyValuePairs.ToList().CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair<TKey, TValue> 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<TKey> Keys { get { return _backing.Keys.OfType<TKey>().ToList(); } } public ICollection<TValue> Values { get { return _backing.Values.OfType<TValue>().ToList(); } } } (这也意味着访问未分配的键会引发异常),并且应该是可序列化的。

它被命名为&#39; IndexedDictionary&#39;关于阿德里安的回答。

因人而异。

{{1}}

答案 6 :(得分:1)

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

答案 7 :(得分:1)

保留插入的通用键/值对的另一个选项是使用类似:

Queue<KeyValuePair<string, string>>

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

答案 8 :(得分:1)

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

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

OrderedDictionary<K,V>集合:

  • 基于经典Dictionary<K,V>的源代码(来自.NET Core)
  • 保留插入顺序并允许手动重新排序
  • 功能反向枚举
  • Dictionary<K,V>
  • 具有相同的操作复杂性Add相比,
  • RemoveDictionary<K,V>操作速度降低约20%
  • 每个项目消耗8个字节的内存

答案 9 :(得分:-3)

代码:

//A SortedDictionary is sorted on the key (not value)
System.Collections.Generic.SortedDictionary<string, string> testSortDic = new SortedDictionary<string, string>();

//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<string, string> 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