OrderedDictionary genel uygulaması yok?


136

.NET 3.5'te OrderedDictionary( System.Collections.Specializedad alanında genel bir uygulama) görünmüyor . Kaçırdığım bir şey var mı?

Ben işlevsellik sağlamak için uygulamaları bulduk, ancak / genel bir uygulama olup olmadığını ve kimse .NET 4.0 bir şey olup olmadığını bilmek merak?


1
İşte bir uygulaması OrderedDictionary<T>: codeproject.com/Articles/18615/…
Tim Schmelter


OrderedDictionary <T> uygulamamda, ekleme sırasını korumak için ArrayList yerine LinkedList kullanıldığı için O (1) insert / delete var: clintonbrennan.com/2013/12/…
Clinton

2
Girişleri eklendikleri sırayla yinelemeniz gerekiyorsa, List <KeyValuePair <TKey, TValue >> yeterince iyi olabilir. (Genel bir çözüm değil, bazı amaçlar için yeterince iyi.)
yoyo

1
Bu talihsiz bir ihmal. Başka iyi veri türleri de vardır Systems.Collections.Generic. Let isteği OrderedDictionary<TKey,TValue>Diğerleri işaret olarak .NET 5. için, vaka anahtar bir int dejenere olduğunu ve özel bakıma ihtiyaç olacaktır.
Albay Panik

Yanıtlar:


60

Haklısın. Jenerik eşdeğeri yokOrderedDictionaryÇerçevenin kendisinde .

(Halen bildiğim kadarıyla .NET 4 için de durum böyle.)

Ancak buna Visual Studio'nun UserVoice (2016-10-04) 'inde oy verebilirsiniz !


95

Bir jenerik uygulamak son derece OrderedDictionaryzor değildir, ancak gereksiz yere zaman alıcıdır ve açıkçası bu sınıf Microsoft açısından büyük bir gözetimdir. Bunu uygulamanın birçok yolu vardır, ancak KeyedCollectiondahili depolama birimim için bir kullanmayı seçtim . Ben de List<T>aslında bir melez IList ve IDictionary olduğu için bu şekilde sıralamak için çeşitli yöntemler uygulamayı seçti . Uygulamamı gelecek nesiller için buraya dahil ettim.

İşte arayüz. System.Collections.Specialized.IOrderedDictionaryMicrosoft tarafından sağlanan bu arabirimin genel olmayan sürümü olan içerdiğine dikkat edin .

// http://unlicense.org
using System;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace mattmc3.Common.Collections.Generic {

    public interface IOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IOrderedDictionary {
        new TValue this[int index] { get; set; }
        new TValue this[TKey key] { get; set; }
        new int Count { get; }
        new ICollection<TKey> Keys { get; }
        new ICollection<TValue> Values { get; }
        new void Add(TKey key, TValue value);
        new void Clear();
        void Insert(int index, TKey key, TValue value);
        int IndexOf(TKey key);
        bool ContainsValue(TValue value);
        bool ContainsValue(TValue value, IEqualityComparer<TValue> comparer);
        new bool ContainsKey(TKey key);
        new IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();
        new bool Remove(TKey key);
        new void RemoveAt(int index);
        new bool TryGetValue(TKey key, out TValue value);
        TValue GetValue(TKey key);
        void SetValue(TKey key, TValue value);
        KeyValuePair<TKey, TValue> GetItem(int index);
        void SetItem(int index, TValue value);
    }

}

Yardımcı sınıflarla birlikte uygulama:

// http://unlicense.org
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;

namespace mattmc3.Common.Collections.Generic {

    /// <summary>
    /// A dictionary object that allows rapid hash lookups using keys, but also
    /// maintains the key insertion order so that values can be retrieved by
    /// key index.
    /// </summary>
    public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue> {

        #region Fields/Properties

        private KeyedCollection2<TKey, KeyValuePair<TKey, TValue>> _keyedCollection;

        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key associated with the value to get or set.</param>
        public TValue this[TKey key] {
            get {
                return GetValue(key);
            }
            set {
                SetValue(key, value);
            }
        }

        /// <summary>
        /// Gets or sets the value at the specified index.
        /// </summary>
        /// <param name="index">The index of the value to get or set.</param>
        public TValue this[int index] {
            get {
                return GetItem(index).Value;
            }
            set {
                SetItem(index, value);
            }
        }

        public int Count {
            get { return _keyedCollection.Count; }
        }

        public ICollection<TKey> Keys {
            get {
                return _keyedCollection.Select(x => x.Key).ToList();
            }
        }

        public ICollection<TValue> Values {
            get {
                return _keyedCollection.Select(x => x.Value).ToList();
            }
        }

        public IEqualityComparer<TKey> Comparer {
            get;
            private set;
        }

        #endregion

        #region Constructors

        public OrderedDictionary() {
            Initialize();
        }

        public OrderedDictionary(IEqualityComparer<TKey> comparer) {
            Initialize(comparer);
        }

        public OrderedDictionary(IOrderedDictionary<TKey, TValue> dictionary) {
            Initialize();
            foreach (KeyValuePair<TKey, TValue> pair in dictionary) {
                _keyedCollection.Add(pair);
            }
        }

        public OrderedDictionary(IOrderedDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) {
            Initialize(comparer);
            foreach (KeyValuePair<TKey, TValue> pair in dictionary) {
                _keyedCollection.Add(pair);
            }
        }

        #endregion

        #region Methods

        private void Initialize(IEqualityComparer<TKey> comparer = null) {
            this.Comparer = comparer;
            if (comparer != null) {
                _keyedCollection = new KeyedCollection2<TKey, KeyValuePair<TKey, TValue>>(x => x.Key, comparer);
            }
            else {
                _keyedCollection = new KeyedCollection2<TKey, KeyValuePair<TKey, TValue>>(x => x.Key);
            }
        }

        public void Add(TKey key, TValue value) {
            _keyedCollection.Add(new KeyValuePair<TKey, TValue>(key, value));
        }

        public void Clear() {
            _keyedCollection.Clear();
        }

        public void Insert(int index, TKey key, TValue value) {
            _keyedCollection.Insert(index, new KeyValuePair<TKey, TValue>(key, value));
        }

        public int IndexOf(TKey key) {
            if (_keyedCollection.Contains(key)) {
                return _keyedCollection.IndexOf(_keyedCollection[key]);
            }
            else {
                return -1;
            }
        }

        public bool ContainsValue(TValue value) {
            return this.Values.Contains(value);
        }

        public bool ContainsValue(TValue value, IEqualityComparer<TValue> comparer) {
            return this.Values.Contains(value, comparer);
        }

        public bool ContainsKey(TKey key) {
            return _keyedCollection.Contains(key);
        }

        public KeyValuePair<TKey, TValue> GetItem(int index) {
            if (index < 0 || index >= _keyedCollection.Count) {
                throw new ArgumentException(String.Format("The index was outside the bounds of the dictionary: {0}", index));
            }
            return _keyedCollection[index];
        }

        /// <summary>
        /// Sets the value at the index specified.
        /// </summary>
        /// <param name="index">The index of the value desired</param>
        /// <param name="value">The value to set</param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown when the index specified does not refer to a KeyValuePair in this object
        /// </exception>
        public void SetItem(int index, TValue value) {
            if (index < 0 || index >= _keyedCollection.Count) {
                throw new ArgumentException("The index is outside the bounds of the dictionary: {0}".FormatWith(index));
            }
            var kvp = new KeyValuePair<TKey, TValue>(_keyedCollection[index].Key, value);
            _keyedCollection[index] = kvp;
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
            return _keyedCollection.GetEnumerator();
        }

        public bool Remove(TKey key) {
            return _keyedCollection.Remove(key);
        }

        public void RemoveAt(int index) {
            if (index < 0 || index >= _keyedCollection.Count) {
                throw new ArgumentException(String.Format("The index was outside the bounds of the dictionary: {0}", index));
            }
            _keyedCollection.RemoveAt(index);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key associated with the value to get.</param>
        public TValue GetValue(TKey key) {
            if (_keyedCollection.Contains(key) == false) {
                throw new ArgumentException("The given key is not present in the dictionary: {0}".FormatWith(key));
            }
            var kvp = _keyedCollection[key];
            return kvp.Value;
        }

        /// <summary>
        /// Sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key associated with the value to set.</param>
        /// <param name="value">The the value to set.</param>
        public void SetValue(TKey key, TValue value) {
            var kvp = new KeyValuePair<TKey, TValue>(key, value);
            var idx = IndexOf(key);
            if (idx > -1) {
                _keyedCollection[idx] = kvp;
            }
            else {
                _keyedCollection.Add(kvp);
            }
        }

        public bool TryGetValue(TKey key, out TValue value) {
            if (_keyedCollection.Contains(key)) {
                value = _keyedCollection[key].Value;
                return true;
            }
            else {
                value = default(TValue);
                return false;
            }
        }

        #endregion

        #region sorting
        public void SortKeys() {
            _keyedCollection.SortByKeys();
        }

        public void SortKeys(IComparer<TKey> comparer) {
            _keyedCollection.SortByKeys(comparer);
        }

        public void SortKeys(Comparison<TKey> comparison) {
            _keyedCollection.SortByKeys(comparison);
        }

        public void SortValues() {
            var comparer = Comparer<TValue>.Default;
            SortValues(comparer);
        }

        public void SortValues(IComparer<TValue> comparer) {
            _keyedCollection.Sort((x, y) => comparer.Compare(x.Value, y.Value));
        }

        public void SortValues(Comparison<TValue> comparison) {
            _keyedCollection.Sort((x, y) => comparison(x.Value, y.Value));
        }
        #endregion

        #region IDictionary<TKey, TValue>

        void IDictionary<TKey, TValue>.Add(TKey key, TValue value) {
            Add(key, value);
        }

        bool IDictionary<TKey, TValue>.ContainsKey(TKey key) {
            return ContainsKey(key);
        }

        ICollection<TKey> IDictionary<TKey, TValue>.Keys {
            get { return Keys; }
        }

        bool IDictionary<TKey, TValue>.Remove(TKey key) {
            return Remove(key);
        }

        bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value) {
            return TryGetValue(key, out value);
        }

        ICollection<TValue> IDictionary<TKey, TValue>.Values {
            get { return Values; }
        }

        TValue IDictionary<TKey, TValue>.this[TKey key] {
            get {
                return this[key];
            }
            set {
                this[key] = value;
            }
        }

        #endregion

        #region ICollection<KeyValuePair<TKey, TValue>>

        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) {
            _keyedCollection.Add(item);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.Clear() {
            _keyedCollection.Clear();
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) {
            return _keyedCollection.Contains(item);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
            _keyedCollection.CopyTo(array, arrayIndex);
        }

        int ICollection<KeyValuePair<TKey, TValue>>.Count {
            get { return _keyedCollection.Count; }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
            get { return false; }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) {
            return _keyedCollection.Remove(item);
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey, TValue>>

        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() {
            return GetEnumerator();
        }

        #endregion

        #region IEnumerable

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        #endregion

        #region IOrderedDictionary

        IDictionaryEnumerator IOrderedDictionary.GetEnumerator() {
            return new DictionaryEnumerator<TKey, TValue>(this);
        }

        void IOrderedDictionary.Insert(int index, object key, object value) {
            Insert(index, (TKey)key, (TValue)value);
        }

        void IOrderedDictionary.RemoveAt(int index) {
            RemoveAt(index);
        }

        object IOrderedDictionary.this[int index] {
            get {
                return this[index];
            }
            set {
                this[index] = (TValue)value;
            }
        }

        #endregion

        #region IDictionary

        void IDictionary.Add(object key, object value) {
            Add((TKey)key, (TValue)value);
        }

        void IDictionary.Clear() {
            Clear();
        }

        bool IDictionary.Contains(object key) {
            return _keyedCollection.Contains((TKey)key);
        }

        IDictionaryEnumerator IDictionary.GetEnumerator() {
            return new DictionaryEnumerator<TKey, TValue>(this);
        }

        bool IDictionary.IsFixedSize {
            get { return false; }
        }

        bool IDictionary.IsReadOnly {
            get { return false; }
        }

        ICollection IDictionary.Keys {
            get { return (ICollection)this.Keys; }
        }

        void IDictionary.Remove(object key) {
            Remove((TKey)key);
        }

        ICollection IDictionary.Values {
            get { return (ICollection)this.Values; }
        }

        object IDictionary.this[object key] {
            get {
                return this[(TKey)key];
            }
            set {
                this[(TKey)key] = (TValue)value;
            }
        }

        #endregion

        #region ICollection

        void ICollection.CopyTo(Array array, int index) {
            ((ICollection)_keyedCollection).CopyTo(array, index);
        }

        int ICollection.Count {
            get { return ((ICollection)_keyedCollection).Count; }
        }

        bool ICollection.IsSynchronized {
            get { return ((ICollection)_keyedCollection).IsSynchronized; }
        }

        object ICollection.SyncRoot {
            get { return ((ICollection)_keyedCollection).SyncRoot; }
        }

        #endregion
    }

    public class KeyedCollection2<TKey, TItem> : KeyedCollection<TKey, TItem> {
        private const string DelegateNullExceptionMessage = "Delegate passed cannot be null";
        private Func<TItem, TKey> _getKeyForItemDelegate;

        public KeyedCollection2(Func<TItem, TKey> getKeyForItemDelegate)
            : base() {
            if (getKeyForItemDelegate == null) throw new ArgumentNullException(DelegateNullExceptionMessage);
            _getKeyForItemDelegate = getKeyForItemDelegate;
        }

        public KeyedCollection2(Func<TItem, TKey> getKeyForItemDelegate, IEqualityComparer<TKey> comparer)
            : base(comparer) {
            if (getKeyForItemDelegate == null) throw new ArgumentNullException(DelegateNullExceptionMessage);
            _getKeyForItemDelegate = getKeyForItemDelegate;
        }

        protected override TKey GetKeyForItem(TItem item) {
            return _getKeyForItemDelegate(item);
        }

        public void SortByKeys() {
            var comparer = Comparer<TKey>.Default;
            SortByKeys(comparer);
        }

        public void SortByKeys(IComparer<TKey> keyComparer) {
            var comparer = new Comparer2<TItem>((x, y) => keyComparer.Compare(GetKeyForItem(x), GetKeyForItem(y)));
            Sort(comparer);
        }

        public void SortByKeys(Comparison<TKey> keyComparison) {
            var comparer = new Comparer2<TItem>((x, y) => keyComparison(GetKeyForItem(x), GetKeyForItem(y)));
            Sort(comparer);
        }

        public void Sort() {
            var comparer = Comparer<TItem>.Default;
            Sort(comparer);
        }

        public void Sort(Comparison<TItem> comparison) {
            var newComparer = new Comparer2<TItem>((x, y) => comparison(x, y));
            Sort(newComparer);
        }

        public void Sort(IComparer<TItem> comparer) {
            List<TItem> list = base.Items as List<TItem>;
            if (list != null) {
                list.Sort(comparer);
            }
        }
    }

    public class Comparer2<T> : Comparer<T> {
        //private readonly Func<T, T, int> _compareFunction;
        private readonly Comparison<T> _compareFunction;

        #region Constructors

        public Comparer2(Comparison<T> comparison) {
            if (comparison == null) throw new ArgumentNullException("comparison");
            _compareFunction = comparison;
        }

        #endregion

        public override int Compare(T arg1, T arg2) {
            return _compareFunction(arg1, arg2);
        }
    }

    public class DictionaryEnumerator<TKey, TValue> : IDictionaryEnumerator, IDisposable {
        readonly IEnumerator<KeyValuePair<TKey, TValue>> impl;
        public void Dispose() { impl.Dispose(); }
        public DictionaryEnumerator(IDictionary<TKey, TValue> value) {
            this.impl = value.GetEnumerator();
        }
        public void Reset() { impl.Reset(); }
        public bool MoveNext() { return impl.MoveNext(); }
        public DictionaryEntry Entry {
            get {
                var pair = impl.Current;
                return new DictionaryEntry(pair.Key, pair.Value);
            }
        }
        public object Key { get { return impl.Current.Key; } }
        public object Value { get { return impl.Current.Value; } }
        public object Current { get { return Entry; } }
    }
}

Ve hiçbir uygulama birkaç test olmadan tamamlanmayacaktır (ama trajik bir şekilde, SO bir yazıya bu kadar kod göndermeme izin vermeyecek), bu yüzden testlerinizi yazmak için sizi terk etmek zorunda kalacağım. Ancak, nasıl çalıştığı hakkında bir fikir edinebilmeniz için birkaçını bıraktım:

// http://unlicense.org
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using mattmc3.Common.Collections.Generic;

namespace mattmc3.Tests.Common.Collections.Generic {
    [TestClass]
    public class OrderedDictionaryTests {

        private OrderedDictionary<string, string> GetAlphabetDictionary(IEqualityComparer<string> comparer = null) {
            OrderedDictionary<string, string> alphabet = (comparer == null ? new OrderedDictionary<string, string>() : new OrderedDictionary<string, string>(comparer));
            for (var a = Convert.ToInt32('a'); a <= Convert.ToInt32('z'); a++) {
                var c = Convert.ToChar(a);
                alphabet.Add(c.ToString(), c.ToString().ToUpper());
            }
            Assert.AreEqual(26, alphabet.Count);
            return alphabet;
        }

        private List<KeyValuePair<string, string>> GetAlphabetList() {
            var alphabet = new List<KeyValuePair<string, string>>();
            for (var a = Convert.ToInt32('a'); a <= Convert.ToInt32('z'); a++) {
                var c = Convert.ToChar(a);
                alphabet.Add(new KeyValuePair<string, string>(c.ToString(), c.ToString().ToUpper()));
            }
            Assert.AreEqual(26, alphabet.Count);
            return alphabet;
        }

        [TestMethod]
        public void TestAdd() {
            var od = new OrderedDictionary<string, string>();
            Assert.AreEqual(0, od.Count);
            Assert.AreEqual(-1, od.IndexOf("foo"));

            od.Add("foo", "bar");
            Assert.AreEqual(1, od.Count);
            Assert.AreEqual(0, od.IndexOf("foo"));
            Assert.AreEqual(od[0], "bar");
            Assert.AreEqual(od["foo"], "bar");
            Assert.AreEqual(od.GetItem(0).Key, "foo");
            Assert.AreEqual(od.GetItem(0).Value, "bar");
        }

        [TestMethod]
        public void TestRemove() {
            var od = new OrderedDictionary<string, string>();

            od.Add("foo", "bar");
            Assert.AreEqual(1, od.Count);

            od.Remove("foo");
            Assert.AreEqual(0, od.Count);
        }

        [TestMethod]
        public void TestRemoveAt() {
            var od = new OrderedDictionary<string, string>();

            od.Add("foo", "bar");
            Assert.AreEqual(1, od.Count);

            od.RemoveAt(0);
            Assert.AreEqual(0, od.Count);
        }

        [TestMethod]
        public void TestClear() {
            var od = GetAlphabetDictionary();
            Assert.AreEqual(26, od.Count);
            od.Clear();
            Assert.AreEqual(0, od.Count);
        }

        [TestMethod]
        public void TestOrderIsPreserved() {
            var alphabetDict = GetAlphabetDictionary();
            var alphabetList = GetAlphabetList();
            Assert.AreEqual(26, alphabetDict.Count);
            Assert.AreEqual(26, alphabetList.Count);

            var keys = alphabetDict.Keys.ToList();
            var values = alphabetDict.Values.ToList();

            for (var i = 0; i < 26; i++) {
                var dictItem = alphabetDict.GetItem(i);
                var listItem = alphabetList[i];
                var key = keys[i];
                var value = values[i];

                Assert.AreEqual(dictItem, listItem);
                Assert.AreEqual(key, listItem.Key);
                Assert.AreEqual(value, listItem.Value);
            }
        }

        [TestMethod]
        public void TestTryGetValue() {
            var alphabetDict = GetAlphabetDictionary();
            string result = null;
            Assert.IsFalse(alphabetDict.TryGetValue("abc", out result));
            Assert.IsNull(result);
            Assert.IsTrue(alphabetDict.TryGetValue("z", out result));
            Assert.AreEqual("Z", result);
        }

        [TestMethod]
        public void TestEnumerator() {
            var alphabetDict = GetAlphabetDictionary();

            var keys = alphabetDict.Keys.ToList();
            Assert.AreEqual(26, keys.Count);

            var i = 0;
            foreach (var kvp in alphabetDict) {
                var value = alphabetDict[kvp.Key];
                Assert.AreEqual(kvp.Value, value);
                i++;
            }
        }

        [TestMethod]
        public void TestInvalidIndex() {
            var alphabetDict = GetAlphabetDictionary();
            try {
                var notGonnaWork = alphabetDict[100];
                Assert.IsTrue(false, "Exception should have thrown");
            }
            catch (Exception ex) {
                Assert.IsTrue(ex.Message.Contains("index is outside the bounds"));
            }
        }

        [TestMethod]
        public void TestMissingKey() {
            var alphabetDict = GetAlphabetDictionary();
            try {
                var notGonnaWork = alphabetDict["abc"];
                Assert.IsTrue(false, "Exception should have thrown");
            }
            catch (Exception ex) {
                Assert.IsTrue(ex.Message.Contains("key is not present"));
            }
        }

        [TestMethod]
        public void TestUpdateExistingValue() {
            var alphabetDict = GetAlphabetDictionary();
            Assert.IsTrue(alphabetDict.ContainsKey("c"));
            Assert.AreEqual(2, alphabetDict.IndexOf("c"));
            Assert.AreEqual(alphabetDict[2], "C");
            alphabetDict[2] = "CCC";
            Assert.IsTrue(alphabetDict.ContainsKey("c"));
            Assert.AreEqual(2, alphabetDict.IndexOf("c"));
            Assert.AreEqual(alphabetDict[2], "CCC");
        }

        [TestMethod]
        public void TestInsertValue() {
            var alphabetDict = GetAlphabetDictionary();
            Assert.IsTrue(alphabetDict.ContainsKey("c"));
            Assert.AreEqual(2, alphabetDict.IndexOf("c"));
            Assert.AreEqual(alphabetDict[2], "C");
            Assert.AreEqual(26, alphabetDict.Count);
            Assert.IsFalse(alphabetDict.ContainsValue("ABC"));

            alphabetDict.Insert(2, "abc", "ABC");
            Assert.IsTrue(alphabetDict.ContainsKey("c"));
            Assert.AreEqual(2, alphabetDict.IndexOf("abc"));
            Assert.AreEqual(alphabetDict[2], "ABC");
            Assert.AreEqual(27, alphabetDict.Count);
            Assert.IsTrue(alphabetDict.ContainsValue("ABC"));
        }

        [TestMethod]
        public void TestValueComparer() {
            var alphabetDict = GetAlphabetDictionary();
            Assert.IsFalse(alphabetDict.ContainsValue("a"));
            Assert.IsTrue(alphabetDict.ContainsValue("a", StringComparer.OrdinalIgnoreCase));
        }

        [TestMethod]
        public void TestSortByKeys() {
            var alphabetDict = GetAlphabetDictionary();
            var reverseAlphabetDict = GetAlphabetDictionary();
            Comparison<string> stringReverse = ((x, y) => (String.Equals(x, y) ? 0 : String.Compare(x, y) >= 1 ? -1 : 1));
            reverseAlphabetDict.SortKeys(stringReverse);
            for (int j = 0, k = 25; j < alphabetDict.Count; j++, k--) {
                var ascValue = alphabetDict.GetItem(j);
                var dscValue = reverseAlphabetDict.GetItem(k);
                Assert.AreEqual(ascValue.Key, dscValue.Key);
                Assert.AreEqual(ascValue.Value, dscValue.Value);
            }
        }

-- GÜNCELLEME --

Bu ve diğer gerçekten yararlı eksik çekirdek .NET kütüphaneleri için kaynak burada: https://github.com/mattmc3/dotmore/blob/master/dotmore/Collections/Generic/OrderedDictionary.cs


6
Kamu malı olarak, kullanıp kullanamayacağınızı mı, değiştirip değiştirmeyeceğinizi mi soruyorsunuz? Özgür hissedin. Lisansını kastediyor ve bir yere ev sahipliği yapıyorsanız - hayır ... burada sadece şimdilik SO'da yaşıyor.
mattmc3 5:13

3
@ mattmc3 Kodunuz için teşekkür ederiz efendim, ancak kamu malı sorunları hakkındaki yorumunuz beni ilgilendiriyor, yorumda söylediğinizde: "Lisanslı ve bir yere ev sahipliği yapmayı kastediyorsanız - hayır ... burada sadece şimdilik SO'da yaşıyor. " Yazara tüm saygıyla (gerçekten kastedilen), kodu SO'ya gönderdikten sonra gerçekten bu kısıtlamayı yapma hakkınız var mı ??? Örneğin, herhangi birimiz gerçekten SO kodumuzu örneğin git gist olarak yayınlanmasını kısıtlama hakkına sahip miyiz? Kimse?
Nicholas Petersen

6
@NicholasPetersen - Sanırım yanlış anladın. Albay Paniç'e doğrudan yanıt olarak, kişisel olarak lisanslamadığım veya başka bir yere ev sahipliği yapmadığımı bildirdim . (Aslında, bahsettiğinizden beri, bir gist yaptım: gist.github.com/mattmc3/6486878 ). Ancak bu lisanssız koddur. Al ve onunla istediğini yap. Kendi kişisel kullanımım için% 100 yazdım. Numarasız. Zevk almak. Aslında, Microsoft'tan biri bunu okursa, görevlerini tam olarak yapmasını beklerim ve sonunda .NET'in bir sonraki sürümüne koyarım. Atıf gerekmez.
mattmc3

2
Ya TKeyolduğunu int? this[]Böyle bir durumda nasıl çalışacak?
VB

2
@klicker - Normal dizi stili dizinini kullanın. Ekleme talimatı, liste gibi korunur. Tür dönüşümü, bir int ile dizin oluşturmayı veya anahtarın türü aracılığıyla başvurmayı amaçlayıp belirlemediğinizi belirler. Anahtarın türü bir int ise, GetValue () yöntemini kullanmanız gerekir.
mattmc3

32

Kayıt için, nesnelerin bir int ve bir anahtarla dizine eklenmesine izin veren genel bir KeyedCollection vardır. Anahtarın değere gömülmesi gerekir.


2
Bu, OrderedDictionary gibi başlatma sırasını korumaz! Cevabımı kontrol et.
JoelFan

14
Ekleme / yerleştirme sırasını korur.
Guillaume

evet öyle .. keyedcollection öğeleri sıralar bu fikir var nerede çocuklar ... ben bu ikinci kez tökezledi
Boppity Bop

1
Kesinlikle başlatma sırasını korur. Yararlı bağlantılar arasında stackoverflow.com/a/11802824/9344 ve geekswithblogs.net/NewThingsILearned/archive/2010/01/07/… .
Ted

+1, Bu çerçevedeki en iyi çözüm gibi görünüyor. Yine de, kullanmak istediğiniz her tür çifti için soyut sınıfı uygulamak zorunda kalmanız bir tür sürüklemedir. Bir arabirim gerektiren tek bir genel uygulama ile yapabilirsiniz, ancak daha sonra depolamak istediğiniz her tipe arabirimi uygulamanız gerekir.
DCShannon

19

İşte tuhaf bir bulma: System.Web.Extensions.dll içindeki System.Web.Util ad alanı genel bir OrderedDictionary içerir

// Type: System.Web.Util.OrderedDictionary`2
// Assembly: System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Web.Extensions.dll

namespace System.Web.Util
{
    internal class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable

Neden MS System.Collections.Generic paketi yerine oraya yerleştirdiğinden emin değilim, ancak kodu kopyalayıp yapıştırabileceğinizi varsayalım (dahili, bu yüzden doğrudan kullanamazsınız). Uygulama standart bir sözlük ve ayrı Anahtar / Değer listeleri kullanıyor gibi görünüyor. Oldukça açık ...


2
System.Runtime.Collectionsayrıca, OrderedDictionary<TKey, TValue>jenerik olmayan versiyonun etrafını saran bir dahili içerir
VB

1
System.Web.Util.OrderedDictionary<TKey, TValue>dahili olarak genel etrafında uygulanır Dictionary. İşin garibi değil IListamaICollection<KeyValuePair<TKey, TValue>>
Mikhail

1
@rboy Dediğim gibi - dahili ve jenerik olmayan uygulama sarılmış. Ama 3+ yıl önceydi ... Yüzlerce doğrusal aramanın altındaki boyutlar List<KeyValuePair<TKey,TValue>>için, bellek erişim düzeni nedeniyle rekabetçi olacak, daha büyük boyutlar için sadece aynı listeyi + Dictionary<TKey,int>arama olarak kullanın . AFAIK, BigO'da hız / bellek açısından daha iyisini yapan böyle bir veri yapısı yoktur.
VB

1
@rboy burada jenerik olanın bağlantısı , HashTable kullanan jenerik olmayan olana atıfta bulunuyor . Gerçekten küçük listeler için genel Liste / Diziler üzerinde doğrusal arama kullanarak daha hızlı olacağını bahis.
VB

1
@PeterMortensen System.Collections.Specialized.OrderedDictionarygenel bir tür değildir. Bakın, bağladığınız dokümanda köşeli parantez yok: P
user7610

17

Değeri için, işte nasıl çözdüm:

   public class PairList<TKey, TValue> : List<KeyValuePair<TKey, TValue>> {
        Dictionary<TKey, int> itsIndex = new Dictionary<TKey, int>();

        public void Add(TKey key, TValue value) {
            Add(new KeyValuePair<TKey, TValue>(key, value));
            itsIndex.Add(key, Count-1);
        }

        public TValue Get(TKey key) {
            var idx = itsIndex[key];
            return this[idx].Value;
        }
    }

Bu şekilde başlatılabilir:

var pairList = new PairList<string, string>
    {
        { "pitcher", "Ken" },
        { "catcher", "Brad"},
        { "left fielder", "Stan"},
    };

ve şu şekilde erişildi:

foreach (var pair in pairList)
{
    Console.WriteLine("position: {0}, player: {1}",
        pair.Key, pair.Value);
}

// Guaranteed to print in the order of initialization

3
Teşekkürler! Koleksiyon başlatıcıların sadece Addyöntemler için özel bir sözdizimi olduğunu fark etmemiştim .
Sam

10
Bu bir sözlük değil. Sözlük anahtarlı indeksleme anlamına gelir ve yinelenen anahtar yoktur .
nawfal

yine de, anahtar ile endekslemeye ihtiyacınız yoksa (ki bu eklemek çok zor değildir) ve yinelenen anahtarlar bu
işe yarar

1
Bir sorun var kod çağrıları pairList.Add(new KeyValuePair<K,V>())(yani Listsınıftaki yöntem ). Bu durumda itsIndexsözlük güncellenmez ve şimdi liste ve sözlük senkronize değildir. Bir List.Addyöntem oluşturarak yöntemi gizleyebilir new PairList.Add(KeyValuePair<K,V>)veya miras yerine kompozisyon kullanabilir ve tüm Listyöntemleri tekrar uygulayabilir ... çok daha fazla kod ...
neizan

1
@nawfal, endişenizi gidermek için aşağıdaki gibi bir çek eklenebilir: if (itsindex.Contains(key)) return; başlangıcına Addçiftleri önlemek için
JoelFan

14

Genel bir sürümü ile ilgili önemli bir kavramsal sorun, bir a'nın OrderedDictionarykullanıcılarının OrderedDictionary<TKey,TValue>bunu sayısal olarak intveya a kullanarak arama yapmayı beklemelerini beklemesidir TKey. Tek anahtar türü, Objectgenel olmayanlarda OrderedDictionaryolduğu gibi, dizinleyiciye iletilen argüman türü, ne tür dizinleme işleminin gerçekleştirilip gerçekleştirilmeyeceğini ayırt etmek için yeterli olacaktır. Ancak olduğu gibi, a'nın indeksleyicisinin nasıl OrderedDictionary<int, TValue>davranması gerektiği belirsizdir .

Gibi sınıflar ise Drawing.Pointtavsiye ve parçalı-değişken yapıları değiştirmek mülkiyet ayarlayıcılar kullanarak alanlara ziyade özellikleri ve nakarat olarak değişken unsurlar maruz gerektiğini bir kural takip etmişti this, daha sonra bir OrderedDictionary<TKey,TValue>verimli bir açığa çıkaracak ByIndexbir iade özelliğini Indexerbir başvuru düzenlenen yapı sözlük, ve kimin alıcı ve ayarlayıcı çağırır dizinlenmiş bir özelliği vardı GetByIndexve SetByIndexbunun üzerine. Böylece, MyDict.ByIndex[5] += 3;sözlüğün altıncı öğesine 3 eklemek gibi bir şey söylenebilir .

Ne yazık ki, derleyicinin böyle bir şeyi kabul etmesi için, ByIndexözelliğin her çağrıldığında bir yapı yerine yeni bir sınıf örneği döndürmesini sağlamak, böylece boksdan kaçınarak elde edilecek avantajları ortadan kaldırmak gerekir.

VB.NET, bir adlandırılmış endeksli özelliğini kullanarak bu sorunu etrafında alabilir (şimdiye MyDict.ByIndex[int]üyesi olacağını MyDictdoğrusu gerektiren yerine, MyDict.ByIndexüye olmak içinMyDict indexleyici içerir), ancak C # böyle şeyler izin vermez.

Yine de bir teklif etmek faydalı olabilir OrderedDictionary<TKey,TValue> where TKey:class, ancak ilk etapta jenerik sağlamak için birçok neden değer türleriyle kullanılmasına izin vermekti.


int-Tuşlu anahtarların bir sorun oluşturduğu iyi bir noktaya işaret eder , ancak ilgili SortedList<TKey, TValue>türün örneğini izleyerek önlenebilir : yalnızca anahtarlı destek anahtarları [...]ve .Values[...]dizinle erişim için kullanılmasını gerektirir . ( SortedDictionary<TKey, TValue>aksine, endeksli erişim için optimize edilmemiş .ElementAt(...).Value
olanların

7

Bir çok amaç için ben bir ile alabilirsiniz buldum List<KeyValuePair<K, V>>. (Uzatmaya ihtiyacınız varsa Dictionary, açıkçası ve O (n) anahtar / değer aramasından daha iyi bir şeye ihtiyacınız yoksa değil.)


Aynı sonuca kendim geldim!
Peter

1
Dediğim gibi, "birçok amaç için."
David Moles

2
Ayrıca Tuple<T1, T2>, anahtar / değer çifti ilişkisi yoksa bir de kullanabilirsiniz .
cdmckay

1
Anahtarla dizine ekleyemiyorsanız, anahtar / değer çiftlerine sahip olmanın anlamı nedir?
DCShannon

1
@DCShannon Anahtarları değerlerle eşleştirmeye devam edebilirsiniz, onları O (n) 'den daha hızlı arayamazsınız (veya yinelenen anahtarlarla otomatik olarak ilgilenemezsiniz). N'nin küçük değerleri için bazen yeterince iyi, özellikle de tüm anahtarları yinelediğiniz durumlarda.
David Moles

5

Var SortedDictionary<TKey, TValue>. Anlamsal olarak yakın olsa da, bunun OrderedDictionarybasit olmadıkları için aynı olduğunu iddia etmiyorum. Performans özelliklerinden bile. Bununla birlikte Dictionary<TKey, TValue>(ve OrderedDictionarycevaplarda sağlanan uygulamalar ve bu kapsamda çok ilginç ve oldukça önemli bir fark ) ve SortedDictionaryikincisinin altında ikili ağaç kullanmasıdır. Sınıfı jenerik sınıfa uygulanan bellek kısıtlamalarından muaf kılan bu kritik ayrımdır. OutOfMemoryExceptionsGenel anahtar / değer çiftleri kümesini işlemek için genel sınıf kullanıldığında atılan bu konuya bakın .

OutOfMemoryException önlemek için Sözlük yapıcısına geçirilen kapasite parametresi için maksimum değeri nasıl anlayabilirim?


A'nın anahtarlarını veya değerlerini SortedDictionary eklendiği sırayla almanın bir yolu var mı ? İşte buna OrderedDictionaryizin verir. Sıralanandan daha farklı bir konsept . (Geçmişte bu hatayı yaptım; OrderedDictionary yapıcısına bir Karşılaştırıcı sağlamanın sıralı hale getireceğini düşündüm, ancak Karşılaştırıcı ile yaptığı tek şey anahtar eşitliğini belirlemektir; örneğin, dize duyarsız karşılaştırıcı, dize duyarsız anahtar aramasına izin verir.)
Steve

5

Doğru, talihsiz bir ihmal. Python'un OrderedDict'ını özledim

Anahtarların ilk yerleştirilme sırasını hatırlayan sözlük. Yeni bir giriş varolan bir girişin üzerine yazarsa, orijinal ekleme konumu değişmeden kalır. Bir girişi silmek ve yeniden girmek onu sonuna kadar taşıyacaktır.

Bu yüzden kendi OrderedDictionary<K,V>sınıfımı C # 'da yazdım . O nasıl çalışır? İki koleksiyon tutar - vanilya sırasız bir sözlük ve düzenli bir anahtar listesi. Bu çözümle, standart sözlük işlemleri hızlı karmaşıklıklarını korur ve dizine göre arama da hızlıdır.

https://gist.github.com/hickford/5137384

İşte arayüz

/// <summary>
/// A dictionary that remembers the order that keys were first inserted. If a new entry overwrites an existing entry, the original insertion position is left unchanged. Deleting an entry and reinserting it will move it to the end.
/// </summary>
/// <typeparam name="TKey">The type of keys</typeparam>
/// <typeparam name="TValue">The type of values</typeparam>
public interface IOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    /// <summary>
    /// The value of the element at the given index.
    /// </summary>
    TValue this[int index] { get; set; }

    /// <summary>
    /// Find the position of an element by key. Returns -1 if the dictionary does not contain an element with the given key.
    /// </summary>
    int IndexOf(TKey key);

    /// <summary>
    /// Insert an element at the given index.
    /// </summary>
    void Insert(int index, TKey key, TValue value);

    /// <summary>
    /// Remove the element at the given index.
    /// </summary>
    void RemoveAt(int index);
}

3

@VB'nin yorumunu takip etmek için System.Runtime.Collections.OrderedDictionary <,> ' nin erişilebilir bir uygulaması . Aslında yansıma ile erişmek ve bir fabrika aracılığıyla sağlamak için gidiyordu ama bu dll hiç çok erişilebilir gibi görünmüyor, bu yüzden sadece kaynak çekti.

Nota bir şey burada dizinleyici atmaz KeyNotFoundException . Bu sözleşmeden kesinlikle nefret ediyorum ve bu uygulamada aldığım 1 özgürlük buydu. Bu sizin için önemliyse, sadece satırını değiştirin return default(TValue);. C # 6 kullanır ( Visual Studio 2013 ile uyumlu )

/// <summary>
///     System.Collections.Specialized.OrderedDictionary is NOT generic.
///     This class is essentially a generic wrapper for OrderedDictionary.
/// </summary>
/// <remarks>
///     Indexer here will NOT throw KeyNotFoundException
/// </remarks>
public class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
{
    private readonly OrderedDictionary _privateDictionary;

    public OrderedDictionary()
    {
        _privateDictionary = new OrderedDictionary();
    }

    public OrderedDictionary(IDictionary<TKey, TValue> dictionary)
    {
        if (dictionary == null) return;

        _privateDictionary = new OrderedDictionary();

        foreach (var pair in dictionary)
        {
            _privateDictionary.Add(pair.Key, pair.Value);
        }
    }

    public bool IsReadOnly => false;
    public int Count => _privateDictionary.Count;
    int ICollection.Count => _privateDictionary.Count;
    object ICollection.SyncRoot => ((ICollection)_privateDictionary).SyncRoot;
    bool ICollection.IsSynchronized => ((ICollection)_privateDictionary).IsSynchronized;

    bool IDictionary.IsFixedSize => ((IDictionary)_privateDictionary).IsFixedSize;
    bool IDictionary.IsReadOnly => _privateDictionary.IsReadOnly;
    ICollection IDictionary.Keys => _privateDictionary.Keys;
    ICollection IDictionary.Values => _privateDictionary.Values;

    void IDictionary.Add(object key, object value)
    {
        _privateDictionary.Add(key, value);
    }

    void IDictionary.Clear()
    {
        _privateDictionary.Clear();
    }

    bool IDictionary.Contains(object key)
    {
        return _privateDictionary.Contains(key);
    }

    IDictionaryEnumerator IDictionary.GetEnumerator()
    {
        return _privateDictionary.GetEnumerator();
    }

    void IDictionary.Remove(object key)
    {
        _privateDictionary.Remove(key);
    }

    object IDictionary.this[object key]
    {
        get { return _privateDictionary[key]; }
        set { _privateDictionary[key] = value; }
    }

    void ICollection.CopyTo(Array array, int index)
    {
        _privateDictionary.CopyTo(array, index);
    }

    public TValue this[TKey key]
    {
        get
        {
            if (key == null) throw new ArgumentNullException(nameof(key));

            if (_privateDictionary.Contains(key))
            {
                return (TValue) _privateDictionary[key];
            }

            return default(TValue);
        }
        set
        {
            if (key == null) throw new ArgumentNullException(nameof(key));

            _privateDictionary[key] = value;
        }
    }

    public ICollection<TKey> Keys
    {
        get
        {
            var keys = new List<TKey>(_privateDictionary.Count);

            keys.AddRange(_privateDictionary.Keys.Cast<TKey>());

            return keys.AsReadOnly();
        }
    }

    public ICollection<TValue> Values
    {
        get
        {
            var values = new List<TValue>(_privateDictionary.Count);

            values.AddRange(_privateDictionary.Values.Cast<TValue>());

            return values.AsReadOnly();
        }
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        Add(item.Key, item.Value);
    }

    public void Add(TKey key, TValue value)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));

        _privateDictionary.Add(key, value);
    }

    public void Clear()
    {
        _privateDictionary.Clear();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        if (item.Key == null || !_privateDictionary.Contains(item.Key))
        {
            return false;
        }

        return _privateDictionary[item.Key].Equals(item.Value);
    }

    public bool ContainsKey(TKey key)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));

        return _privateDictionary.Contains(key);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        if (array == null) throw new ArgumentNullException(nameof(array));
        if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
        if (array.Rank > 1 || arrayIndex >= array.Length
                           || array.Length - arrayIndex < _privateDictionary.Count)
            throw new ArgumentException("Bad Copy ToArray", nameof(array));

        var index = arrayIndex;
        foreach (DictionaryEntry entry in _privateDictionary)
        {
            array[index] = 
                new KeyValuePair<TKey, TValue>((TKey) entry.Key, (TValue) entry.Value);
            index++;
        }
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        foreach (DictionaryEntry entry in _privateDictionary)
        {
            yield return 
                new KeyValuePair<TKey, TValue>((TKey) entry.Key, (TValue) entry.Value);
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        if (false == Contains(item)) return false;

        _privateDictionary.Remove(item.Key);

        return true;
    }

    public bool Remove(TKey key)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));

        if (false == _privateDictionary.Contains(key)) return false;

        _privateDictionary.Remove(key);

        return true;
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));

        var keyExists = _privateDictionary.Contains(key);
        value = keyExists ? (TValue) _privateDictionary[key] : default(TValue);

        return keyExists;
    }
}

GitHub'da istek çekme / tartışma kabul edildi


3

NuGet'te "resmi" bir paket seçeneği arayanlar için genel bir OrderedDictionary uygulaması .NET CoreFX Lab'a kabul edildi. Her şey yolunda giderse, tür sonunda onaylanacak ve ana .NET CoreFX deposuna entegre edilecektir.

Bu uygulamanın reddedilme olasılığı vardır.

Taahhüt edilen uygulamaya burada başvurulabilir https://github.com/dotnet/corefxlab/blob/57be99a176421992e29009701a99a370983329a6/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/OrderedDictionary.cs

Kesinlikle bu tür kullanıma hazır NuGet paketi burada bulunabilir https://www.nuget.org/packages/Microsoft.Experimental.Collections/1.0.6-e190117-3

Veya paketi Visual Studio içine yükleyebilirsiniz. "Microsoft.Experimental.Collections" paketini bulun ve "Yayın öncesi ekle" onay kutusunun seçili olduğundan emin olun.

Tür resmi olarak sunulduysa ve yayınlandığında bu yayını güncelleyecektir.


Ne zaman yayınlanacağını tahmin edebilir misiniz?
mvorisek

Bu kütüphanenin gelişiminde yer almıyorum, bu yüzden ne yazık ki hiçbir fikrim yok. Muhtemelen "onaylandığında" yerleşik bir çerçeve koleksiyonu olacaktır.
charlie

1

OrderedDictionary<TKey, TValue>Etrafa dolanarak SortedList<TKey, TValue>ve özel bir ekleyerek bir genel uygulama yaptım Dictionary<TKey, int> _order. Sonra Comparer<TKey>_order sözlüğüne bir başvuru ileterek dahili bir uygulama oluşturdum . Sonra bu SorperList iç SortedList için kullanın. Bu sınıf yapıcıya iletilen öğelerin sırasını ve ekleme sırasını korur.

Bu uygulama SortedList<TKey, TValue>_order'a ekleme ve çıkarma O (1) olduğu için neredeyse aynı büyük O özelliklerine sahiptir . Her bir eleman (Kısaca 'C # 4' kitabına göre, s. 292, tablo 7-1) 22 (tepegöz) + 4 (int sipariş) + TKey boyutu (8 varsayalım) = 34 ek bellek alanı alacak SortedList<TKey, TValue>İki bayt ek yükü ile birlikte toplam ek yük 36 byte olurken, aynı kitap jenerik olmayanın OrderedDictionary59 bit ek yükü olduğunu söylüyor .

Ben başarılı olursa sorted=truekurucusuna, ardından _order hiç kullanılmaz, OrderedDictionary<TKey, TValue>tam olarak SortedList<TKey, TValue>eğer tüm anlamlı olarak, ambalaj için küçük yük ile.

İçinde çok fazla olmayan büyük referans nesneleri saklayacağım OrderedDictionary<TKey, TValue>, bu yüzden benim için bu ca. 36 bayt ek yükü tolere edilebilir.

Ana kod aşağıdadır. Güncellenmiş kodun tamamı bu özette yer almaktadır .

public class OrderedList<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
{
    private readonly Dictionary<TKey, int> _order;
    private readonly SortedList<TKey, TValue> _internalList;

    private readonly bool _sorted;
    private readonly OrderComparer _comparer;

    public OrderedList(IDictionary<TKey, TValue> dictionary, bool sorted = false)
    {
        _sorted = sorted;

        if (dictionary == null)
            dictionary = new Dictionary<TKey, TValue>();

        if (_sorted)
        {
            _internalList = new SortedList<TKey, TValue>(dictionary);
        }
        else
        {
            _order = new Dictionary<TKey, int>();
            _comparer = new OrderComparer(ref _order);
            _internalList = new SortedList<TKey, TValue>(_comparer);
            // Keep order of the IDictionary
            foreach (var kvp in dictionary)
            {
                Add(kvp);
            }
        }
    }

    public OrderedList(bool sorted = false)
        : this(null, sorted)
    {
    }

    private class OrderComparer : Comparer<TKey>
    {
        public Dictionary<TKey, int> Order { get; set; }

        public OrderComparer(ref Dictionary<TKey, int> order)
        {
            Order = order;
        }

        public override int Compare(TKey x, TKey y)
        {
            var xo = Order[x];
            var yo = Order[y];
            return xo.CompareTo(yo);
        }
    }

    private void ReOrder()
    {
        var i = 0;
        _order = _order.OrderBy(kvp => kvp.Value).ToDictionary(kvp => kvp.Key, kvp => i++);
        _comparer.Order = _order;
        _lastOrder = _order.Values.Max() + 1;
    }

    public void Add(TKey key, TValue value)
    {
        if (!_sorted)
        {
            _order.Add(key, _lastOrder);
            _lastOrder++;

            // Very rare event
            if (_lastOrder == int.MaxValue)
                ReOrder();
        }

        _internalList.Add(key, value);
    }

    public bool Remove(TKey key)
    {
        var result = _internalList.Remove(key);
        if (!_sorted)
            _order.Remove(key);
        return result;
    }

    // Other IDictionary<> + IDictionary members implementation wrapping around _internalList
    // ...
}

OrderedDictionaryEkleme veya silme işlemleriyle ilgili olarak görebileceğim en az dört farklı kullanım durumu vardır: (1) Hiçbir zaman silinme olmayacaktır; (2) silme işlemleri olacaktır, ancak önemli olan, öğelerin eklenen sıraya göre numaralandırılmasıdır; indekse erişime gerek yoktur; (3) bir öğenin sayısal endeksi sabit kalmalı (veya en azından olabilir) ve koleksiyonun ömrü boyunca 2 milyardan fazla öğe eklenmeyecektir, bu nedenle # 7 öğesi kaldırılırsa, bir daha asla madde # 7; (4) bir maddenin endeksi hayatta kalanlara göre sıralanmalıdır.
süper kedi

1
Senaryolar # 1, ile paralel bir dizi kullanılarak işlenebilir Dictionary. Senaryo # 2 ve # 3, her bir öğenin ne zaman eklendiğini söyleyen bir dizin tutmasını ve ondan önce ya da sonra eklenen öğelere bağlantılar vererek ele alınabilir. Senaryo # 4, rasgele sırayla işlemler için O (1) performansına erişememesi gereken gibi görünen tek senaryodur. Kullanım şekillerine bağlı olarak, # 4 çeşitli tembel güncelleme stratejileri kullanılarak yardımcı olabilir (sayıları ağaçta tutun ve düğümü ve üst öğelerini güncellemek yerine bir düğümde geçersiz kılma değişiklikleri yapın).
supercat

1
Dahili SortedList, özel karşılaştırıcı kullanımı nedeniyle ekleme sırasında öğeleri içeriyor. Yavaş olabilir ama numaralandırma hakkındaki yorumunuz yanlış. Numaralandırma ile ilgili testleri göster ...
VB

1
ToDictionary ile hangi hattan bahsediyorsunuz? Dahili listeyi etkilemez, sadece sözlük sipariş eder.
VB

1
@VB Özür dilerim, ikisini de özledim. Böyle test etmedi. O zaman tek sorun toplama ile olur. İki sözlük aramasının yanı sıra iki ekleme. Aşağı oyu iptal edeceğim.
nawfal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.