Generic Key/Value pair collection in that preserves insertion order?

前端 未结 9 1228
刺人心
刺人心 2020-12-09 01:40

I\'m looking for something like a Dictionary however with a guarantee that it preserves insertion order. Since Dictionary is a hashtable, I do not think it does.<

相关标签:
9条回答
  • 2020-12-09 02:19

    If you need constant complexity of Add, Remove, ContainsKey and order preservation, then there's no such generic in .NET Framework 4.5.

    If you're okay with 3rd party code, take a look at my repository (permissive MIT license): https://github.com/OndrejPetrzilka/Rock.Collections

    There's OrderedDictionary<K,V> collection:

    • source code based on classic Dictionary<K,V> (from .NET Core)
    • preserves order of insertions and allows manual reordering
    • features reversed enumeration
    • has same operation complexities as Dictionary<K,V>
    • Add and Remove operations are ~20% slower compared to Dictionary<K,V>
    • consumes 8 more bytes of memory per item
    0 讨论(0)
  • 2020-12-09 02:20

    Here is a wrapper for the non-generic Systems.Collections.Specialized.OrderedDictionary type.

    This type will return keys/value/pairs sequences in insertion order, much like Ruby 2.0 hashes.

    It does not require C#6 magic, conforms to IDictionary<TKey,TValue> (which also means that accessing a non-assigned key throws an exception), and ought to be serializable.

    It is given the name 'IndexedDictionary' per note on Adrian's answer.

    YMMV.

    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(); }
        }
    }
    
    0 讨论(0)
  • 2020-12-09 02:23

    I know you're writing C#, but Java has a class called LinkedHashMap that uses a private LinkedList to maintain the order of insertion of keys. If you can't find a suitable generic solution, perhaps that would be a start on implementing your own.

    0 讨论(0)
提交回复
热议问题