क्रमबद्धता का कोई सामान्य कार्यान्वयन नहीं?


136

.NET 3.5 में OrderedDictionary(जो System.Collections.Specializedनाम स्थान में है) एक सामान्य कार्यान्वयन प्रतीत नहीं होता है । क्या कोई ऐसा है जो मुझे याद आ रहा है?

मैंने कार्यकुशलता प्रदान करने के लिए वहाँ कार्यान्वयन लागू किया है, लेकिन आश्चर्य है कि क्या / क्यों एक सामान्य कार्यान्वयन आउट-ऑफ-द-बॉक्स नहीं है और अगर किसी को पता है कि क्या यह .NET 4.0 में कुछ है?


1
यहाँ एक का कार्यान्वयन है OrderedDictionary<T>: codeproject.com/Articles/18615/…
टिम श्मेल्टर


मेरे आदेश का क्रियान्वयन <T> में O (1) इन्सर्ट है / हटाएं क्योंकि यह सम्मिलन क्रम बनाए रखने के लिए ArrayList के बजाय लिंक्डलिस्ट
क्लिंटन

2
यदि आपको केवल उन प्रविष्टियों में प्रविष्टियों पर पुनरावृत्ति करने में सक्षम होने की आवश्यकता होती है जो वे जोड़ रहे थे तो सूची <KeyValuePair <TKey, TValue >> काफी अच्छी हो सकती है। (दी गई, सामान्य समाधान नहीं, लेकिन कुछ उद्देश्यों के लिए पर्याप्त है।)
योयो

1
यह एक दुर्भाग्यपूर्ण चूक है। अन्य अच्छे डेटा प्रकार हैं Systems.Collections.GenericOrderedDictionary<TKey,TValue>.NET के लिए अनुरोध करते हैं। 5. जैसा कि अन्य ने बताया है कि, कुंजी एक int degenerate है, और विशेष देखभाल की आवश्यकता होगी।
कर्नल पैनिक

जवाबों:


60

आप सही हे। OrderedDictionaryफ्रेमवर्क में कोई सामान्य समान नहीं है।

(यह अभी भी .NET 4 के लिए मामला है, जहाँ तक मुझे जानकारी है।)

लेकिन आप इसके लिए विजुअल स्टूडियो के यूजरवॉइस (2016-10-04) पर वोट कर सकते हैं !


95

जेनेरिक OrderedDictionaryको लागू करना बहुत कठिन नहीं है, लेकिन यह अनावश्यक रूप से समय लेने वाला है और स्पष्ट रूप से यह वर्ग माइक्रोसॉफ्ट के हिस्से पर बहुत बड़ा प्रभाव है। इसे लागू करने के कई तरीके हैं, लेकिन मैंने KeyedCollectionअपने आंतरिक भंडारण के लिए उपयोग करना चुना । मैंने उस तरीके को छाँटने के लिए विभिन्न तरीकों को लागू करना भी चुना List<T>क्योंकि यह अनिवार्य रूप से एक हाइब्रिड IList और IDEDIA है। मैंने पोस्टपेरिटी के लिए अपना कार्यान्वयन यहाँ शामिल किया है।

यहाँ इंटरफ़ेस है। ध्यान दें कि इसमें वह शामिल है System.Collections.Specialized.IOrderedDictionary, जो इस इंटरफ़ेस का गैर-जेनेरिक संस्करण है जो Microsoft द्वारा प्रदान किया गया था।

// 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);
    }

}

यहाँ सहायक वर्गों के साथ कार्यान्वयन है:

// 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; } }
    }
}

और कोई भी परीक्षण कुछ परीक्षणों के बिना पूरा नहीं होगा (लेकिन दुखद यह है कि, एसओ मुझे एक पोस्ट में इतना कोड देने नहीं देगा), इसलिए मुझे आपको अपने परीक्षण लिखने के लिए छोड़ना होगा। लेकिन, मैंने उनमें से कुछ को छोड़ दिया ताकि आप अंदाजा लगा सकें कि यह कैसे काम करता है:

// 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);
            }
        }

-- अपडेट करें --

इस और अन्य वास्तव में उपयोगी लापता कोर नेट पुस्तकालयों यहाँ स्रोत: https://github.com/mattmc3/dotmore/blob/master/dotmore/Collections/Generic/OrderedDictionary.cs


6
सार्वजनिक डोमेन द्वारा, क्या आप पूछ रहे हैं कि क्या आप इसका उपयोग कर सकते हैं, इसे संशोधित कर सकते हैं और इसका इलाज कर सकते हैं जैसे यह आपकी चिंता मुक्त है - हाँ। स्वतंत्र महसूस करना। यदि आप इसे लाइसेंस देने का मतलब है और इसे कहीं पर होस्ट करते हैं - नहीं ... यह केवल यहाँ पर अभी तक रहता है।
मटका 3

3
@ Mattmc3 आपके कोड सर के लिए धन्यवाद, लेकिन सार्वजनिक डोमेन के मुद्दों पर आपकी टिप्पणी मुझे चिंतित करती है, जब आपने टिप्पणी में कहा था: "यदि आपका मतलब है कि इसे लाइसेंस दें और इसे कहीं पर होस्ट करें - नहीं ... यह केवल एसओ के लिए यहां रहता है। " लेखक के लिए सभी सम्मान (सही मायने में) के साथ, क्या आपको वास्तव में उस प्रतिबंध को बनाने का अधिकार है, एक बार जब आपने कोड को पोस्ट किया है ??? जैसे हम में से किसी को वास्तव में एसओ पर अपने कोड को प्रतिबंधित करने का अधिकार है, उदाहरण के लिए, एक जिस्ट के रूप में? किसी को?
निकोलस पीटरसन 16

6
@ निचलोलपेटर्स - मुझे लगता है कि आपको गलत समझा गया। कर्नल पैनिक की सीधी प्रतिक्रिया में, मैंने उन्हें सूचित किया कि मैंने व्यक्तिगत रूप से इसे लाइसेंस नहीं दिया है या इसे कहीं और होस्ट नहीं किया है। (वास्तव में, जब से आपने इसका उल्लेख किया है, मैंने एक जिस्ट बनाया : gist.github.com/mattmc3/64868787 )। लेकिन यह लाइसेंस फ्री कोड है। इसे लो और इसके साथ जो तुम चाहते हो वह करो। मैंने इसे अपने निजी उपयोग के लिए 100% लिखा था। यह अनिर्दिष्ट है। का आनंद लें। वास्तव में, यदि Microsoft का कोई व्यक्ति कभी इसे पढ़ता है, तो मैं उनसे पूरी तरह से अपनी ड्यूटी करने की अपेक्षा करता हूं और अंत में इसे .NET की अगली रिलीज में डाल देता हूं। आवश्यक नहीं है।
मटका 3

2
क्या होगा अगर TKeyहै int? this[]ऐसे में काम कैसे होगा?
VB

2
@ विकर - बस नियमित सरणी शैली अनुक्रमण का उपयोग करें। सूची की तरह ही प्रविष्टि क्रम बनाए रखा जाता है। प्रकार रूपांतरण यह निर्धारित करता है कि क्या आप एक इंट के साथ अनुक्रमणिका या कुंजी के प्रकार के माध्यम से संदर्भ देने के लिए हैं। यदि कुंजी का प्रकार एक int है, तो आपको GetValue () विधि का उपयोग करने की आवश्यकता है।
मटका 3

32

रिकॉर्ड के लिए, एक जेनेरिक KeyedCollection है जो वस्तुओं को एक इंट और एक कुंजी द्वारा अनुक्रमित करने की अनुमति देता है। मूल्य में कुंजी को एम्बेड किया जाना चाहिए।


2
यह ऑर्डरडाउन की तरह आरंभीकरण के क्रम को बनाए नहीं रखता है! मेरे उत्तर की जाँच करें।
जोएलफैन

14
यह ऐड / इंसर्शन के क्रम को बनाए रखता है।
गिलियूम

हाँ, यह करता है .. आप लोगों को यह धारणा कहाँ है कि कीडेक्लेक्शन आइटमों को छाँटता है ... मैं इस दूसरी बार ठोकर खा रहा हूँ
Boppity Bop

1
यह निश्चित रूप से आरंभीकरण के क्रम को बनाए रखता है। सहायक लिंक में stackoverflow.com/a/11802824/9344 और geekswithblogs.net/NewThingsILearned/archive/2010/01/07/… शामिल हैं
टेड

+1, यह फ्रेमवर्क में सबसे अच्छा समाधान की तरह लगता है। प्रत्येक प्रकार के जोड़े के लिए अमूर्त वर्ग को लागू करने के बाद आप जिस तरह का उपयोग करना चाहते हैं, वह एक प्रकार का है, हालांकि। आप इसे एक सामान्य कार्यान्वयन के साथ कर सकते हैं जिसके लिए एक इंटरफ़ेस की आवश्यकता होती है, लेकिन फिर आपको प्रत्येक प्रकार के इंटरफ़ेस को लागू करना होगा जिसे आप स्टोर करना चाहते थे।
DCShannon

19

यहाँ एक विचित्र लगता है: System.Web.Util नाम स्थान में System.Web.Extensions.dll में एक सामान्य क्रमबद्धता शामिल है

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

निश्चित नहीं है कि MS ने System.Collections.Generic पैकेज के बजाय इसे वहां क्यों रखा, लेकिन मुझे लगता है कि आप बस कोड को कॉपी कर सकते हैं और इसका उपयोग कर सकते हैं (यह आंतरिक है, इसलिए इसे सीधे उपयोग नहीं कर सकते हैं)। ऐसा लगता है कि कार्यान्वयन मानक शब्दकोश और अलग-अलग कुंजी / मान सूचियों का उपयोग करता है। बहुत सीधा...


2
System.Runtime.Collectionsइसमें एक आंतरिक भी शामिल है OrderedDictionary<TKey, TValue>जो सिर्फ गैर-जेनेरिक संस्करण के चारों ओर लपेटता है
VB

1
System.Web.Util.OrderedDictionary<TKey, TValue>जेनेरिक के आसपास आंतरिक रूप से लागू किया गया है Dictionary। अजीब तरह से यह लागू नहीं होता है IListलेकिनICollection<KeyValuePair<TKey, TValue>>
मिखाइल

1
@rboy जैसा कि मैंने कहा - यह आंतरिक था और गैर-सामान्य कार्यान्वयन को लपेटता था। लेकिन यह 3+ साल पहले की थी ... List<KeyValuePair<TKey,TValue>>स्मृति के उपयोग पैटर्न के कारण सैकड़ों रैखिक खोज के जोड़े के नीचे के आकार प्रतिस्पर्धी होंगे, बड़े आकार के लिए सिर्फ एक ही सूची + Dictionary<TKey,int>को लुकअप के रूप में उपयोग करें । AFAIK ऐसा कोई डेटास्ट्रक्चर नहीं है जो बिगओ में स्पीड / मेमोरी के मामले में बेहतर करता है।
वीबी

1
@rboy यहाँ जेनेरिक लिंक है , यह गैर-जेनेरिक का संदर्भ देता है जो हैशटेबल का उपयोग करता है। मैं वास्तव में शर्त लगाता हूं कि जेनेरिक सूची / एरे पर रैखिक खोज का उपयोग करने वाले छोटे आकार के लिए तेजी से होगा।
वीबी

1
@PeterMortensen System.Collections.Specialized.OrderedDictionaryएक सामान्य प्रकार नहीं है। देखिए, आपके द्वारा लिंक किए गए doc पृष्ठ पर कोई कोण कोष्ठक नहीं: P
user7610

17

इसके लायक क्या है, यहाँ बताया गया है कि मैंने इसे कैसे हल किया:

   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;
        }
    }

इसे इस तरह शुरू किया जा सकता है:

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

और इस तरह पहुँचा:

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

// Guaranteed to print in the order of initialization

3
धन्यवाद! मुझे महसूस नहीं हुआ कि संग्रह आरम्भिककर्ता केवल Addविधियों के लिए विशेष वाक्यविन्यास थे ।
सैम

10
यह एक शब्दकोश नहीं है। शब्दकोश कुंजी अनुक्रमित और कोई डुप्लिकेट कुंजी के लिए खड़ा है ।
नवफल

फिर भी यदि आपको कुंजी द्वारा अनुक्रमण की आवश्यकता नहीं है (जो जोड़ना बहुत कठिन नहीं है) और डुप्लिकेट कुंजी यह काम में आती है
१६'१४

1
आपको एक समस्या है कोड कॉल pairList.Add(new KeyValuePair<K,V>())(यानी Listकक्षा पर विधि )। उस स्थिति में, itsIndexशब्दकोश अपडेट नहीं किया गया है, और अब सूची और शब्दकोश सिंक से बाहर हैं। एक List.Addविधि बनाकर विधि को छिपा सकते थे new PairList.Add(KeyValuePair<K,V>), या वंशानुक्रम के बजाय रचना का उपयोग कर सकते थे और सभी Listविधियों को फिर से लागू कर सकते थे ... बहुत अधिक कोड ...
neizan

1
@nawfal, अपनी चिंता को दूर करने के लिए, कोई भी एक जांच जोड़ सकता है जैसे: डुप्लिकेट को रोकने if (itsindex.Contains(key)) return;के Addलिए शुरुआत में
JoelFan

14

जेनेरिक संस्करण के साथ एक प्रमुख वैचारिक समस्या OrderedDictionaryयह है कि उपयोगकर्ताओं को OrderedDictionary<TKey,TValue>यह उम्मीद होगी कि यह या तो संख्यात्मक रूप से int, या लुकअप का उपयोग करके इसे इंडेक्स करने में सक्षम होगा TKey। जब एकमात्र प्रकार की कुंजी थी Object, जैसा कि गैर-जेनेरिक के साथ था , OrderedDictionaryतो अनुक्रमणिका को दिए गए तर्क का प्रकार यह भेद करने के लिए पर्याप्त होगा कि किस प्रकार का अनुक्रमण ऑपरेशन किया जाना चाहिए। हालांकि, यह स्पष्ट नहीं है कि किसी व्यक्ति के सूचकांक को कैसे OrderedDictionary<int, TValue>व्यवहार करना चाहिए।

यदि वर्गों Drawing.Pointने सिफारिश की थी और एक नियम का पालन किया था कि टुकड़ा-उत्परिवर्तनीय संरचनाओं को गुणों के बजाय फ़ील्ड के रूप में उनके उत्परिवर्तनीय तत्वों को उजागर करना चाहिए, और संपत्ति के निपटान से संशोधित करने से बचना चाहिए this, तो एक OrderedDictionary<TKey,TValue>ऐसी ByIndexसंपत्ति को कुशलता से उजागर कर सकता है Indexerजिसने एक संदर्भ लौटाया शब्दकोश, और एक अनुक्रमित संपत्ति थी जिसका गेट्टर और सेटर कॉल GetByIndexऔर SetByIndexउस पर होगा। इस प्रकार, कोई MyDict.ByIndex[5] += 3;शब्दकोश के छठे तत्व को जोड़ने के लिए कुछ कह सकता है ।

दुर्भाग्य से, संकलक के लिए इस तरह की बात को स्वीकार करने के लिए, ByIndexसंपत्ति को एक नए वर्ग के उदाहरण के रूप में वापस लाने के बजाय हर बार इसे लागू करने के लिए आवश्यक होगा, बॉक्सिंग से बचने से होने वाले लाभों को समाप्त करना।

VB.NET में, कोई भी नामांकित अनुक्रमित संपत्ति का उपयोग करके उस मुद्दे को प्राप्त कर सकता है (इसलिए MyDict.ByIndex[int]एक सदस्य होगा MyDict, जिसके बजाय MyDict.ByIndexएक सदस्य होना आवश्यक है MyDictजिसमें एक अनुक्रमणिका शामिल है), लेकिन C # ऐसी चीजों की अनुमति नहीं देता है।

यह अभी भी एक पेशकश करने के लिए सार्थक हो सकता है OrderedDictionary<TKey,TValue> where TKey:class, लेकिन पहली जगह में जेनेरिक प्रदान करने के लिए बहुत से कारण मूल्य प्रकारों के साथ उनके उपयोग की अनुमति देना था।


अच्छी बात यह है कि int-typed कुंजियाँ एक चुनौती पेश करती हैं, लेकिन संबंधित SortedList<TKey, TValue>प्रकार के उदाहरण का पालन करके इसे टाला जा सकता है : समर्थन कुंजियाँ केवल साथ [...], और .Values[...]अनुक्रमणिका द्वारा पहुँच के लिए उपयोग की आवश्यकता होती है । ( SortedDictionary<TKey, TValue>, इसके विपरीत, जो अनुक्रमित पहुंच के लिए अनुकूलित नहीं है, इसके उपयोग की आवश्यकता है .ElementAt(...).Value)
mklement0

7

बहुत सारे उद्देश्यों के लिए मैंने पाया है कि एक के साथ मिल सकता है List<KeyValuePair<K, V>>। (यदि आपको इसे विस्तारित करने की आवश्यकता है Dictionary, तो जाहिर है, और यदि आपको ओ (एन) की-वैल्यू लुकअप से बेहतर की आवश्यकता नहीं है।)


बस उसी निष्कर्ष पर पहुंचे खुद!
पीटर

1
जैसा कि मैंने कहा, "बहुत सारे उद्देश्यों के लिए।"
डेविड मोल्स

2
Tuple<T1, T2>यदि उनके पास कुंजी-मूल्य संबंध नहीं है, तो आप उनका उपयोग भी कर सकते हैं ।
cdmckay

1
यदि आप कुंजी द्वारा अनुक्रमित नहीं कर सकते हैं, तो महत्वपूर्ण मूल्य जोड़े होने का क्या मतलब है?
DCShannon

1
@DCShannon आप अभी भी मानों के लिए मैप कर सकते हैं, आप बस उन्हें O (n) से अधिक तेजी से नहीं देख सकते हैं (या डुप्लिकेट कुंजियों के साथ स्वचालित रूप से व्यवहार करें)। N के छोटे मूल्यों के लिए यह कभी-कभी काफी अच्छा होता है, खासकर उन स्थितियों में जहां आप आमतौर पर वैसे भी सभी कुंजियों के माध्यम से पुनरावृत्त होते हैं।
डेविड मोल्स

5

नहीं है SortedDictionary<TKey, TValue>। हालांकि शब्दशः करीब है, मैं यह दावा नहीं कर रहा हूँ कि यह वैसा ही OrderedDictionaryहै क्योंकि वे नहीं हैं। प्रदर्शन विशेषताओं से भी। हालांकि Dictionary<TKey, TValue>(और उस सीमा तक OrderedDictionaryऔर जवाबों में प्रदान की गई कार्यान्वयन) के बीच बहुत दिलचस्प और काफी महत्वपूर्ण अंतर है और SortedDictionaryयह है कि उत्तरार्द्ध नीचे बाइनरी पेड़ का उपयोग कर रहा है। यह महत्वपूर्ण अंतर है क्योंकि यह सामान्य वर्ग के लिए लागू स्मृति बाधाओं के लिए वर्ग प्रतिरक्षा बनाता है। OutOfMemoryExceptionsजब फेंक दिया जाता है तो इस धागे को देखें जब जेनेरिक क्लास का उपयोग कुंजी-मूल्य जोड़े के बड़े सेट को संभालने के लिए किया जाता है।

OutOfMemoryException से बचने के लिए डिक्शनरी निर्माता को दिए गए क्षमता पैरामीटर के अधिकतम मूल्य का पता कैसे लगाएं?


क्या SortedDictionary उनके द्वारा जोड़े गए क्रम में कुंजी या मान प्राप्त करने का कोई तरीका है ? यही OrderedDictionaryअनुमति देता है। अलग अवधारणा से हल कर । (मैंने अतीत में यह गलती की है; मैंने सोचा था कि ऑर्डरडायर्ड कंस्ट्रक्टर की तुलना करने वाले इसे आपूर्ति करना चाहेंगे, लेकिन यह सब तुलना के साथ करता है महत्वपूर्ण समानता निर्धारित करता है; जैसे स्ट्रिंग-असंवेदनशील तुलना स्ट्रिंग-असंवेदनशील कुंजी देखने की अनुमति देता है।)
टूलमेकरसेव

5

सही है, यह एक दुर्भाग्यपूर्ण चूक है। मुझे पाइथन के ऑर्डरडीड याद आती है

एक शब्दकोश जो कि उस आदेश को याद करता है जिसे पहले चाबियाँ डाली गई थीं। यदि कोई नया प्रविष्टि मौजूदा प्रविष्टि को अधिलेखित करता है, तो मूल प्रविष्टि स्थिति अपरिवर्तित रह जाती है। एक प्रविष्टि को हटाना और इसे पुन: स्थापित करना इसे अंत तक ले जाएगा।

इसलिए मैंने OrderedDictionary<K,V>C # में अपनी कक्षा लिखी । यह कैसे काम करता है? यह दो संग्रह रखता है - एक वेनिला अनियंत्रित शब्दकोश और कुंजियों की एक आदेशित सूची। इस समाधान के साथ, मानक शब्दकोश संचालन अपने तेज जटिलताओं को बनाए रखते हैं, और सूचकांक द्वारा देखो भी तेज है।

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

यहाँ इंटरफ़ेस है

/// <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 की टिप्पणी के अनुसरण के रूप में यहाँ System.Runtime.Collections.OrderedDictionary <,> का एक सुलभ कार्यान्वयन है । मैं मूल रूप से इसे प्रतिबिंब द्वारा एक्सेस करने और इसे एक कारखाने के माध्यम से प्रदान करने जा रहा था, लेकिन इस में dll बिल्कुल भी सुलभ नहीं लगता है इसलिए मैंने केवल स्रोत को ही खींच लिया।

ध्यान देने वाली बात यह है कि यहां इंडेक्सर नहीं फेंका जाएगा KeyNotFoundException । मुझे उस अधिवेशन से पूरी तरह से नफरत है और इस कार्यान्वयन में मुझे मिली 1 स्वतंत्रता थी। यदि आपके लिए यह महत्वपूर्ण है, तो बस लाइन को बदल दें return default(TValue);। C # 6 का उपयोग करता है ( विजुअल स्टूडियो 2013 के साथ संगत )

/// <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 पर स्वीकृत अनुरोध / चर्चा खींचिए


3

NuGet में एक "आधिकारिक" पैकेज विकल्प की तलाश करने वालों के लिए, जेनेरिक ऑर्डरडिल्डर का एक कार्यान्वयन .NET CoreFX लैब में स्वीकार किया गया है। यदि सबकुछ ठीक हो जाता है, तो प्रकार अंततः स्वीकृत हो जाएगा और मुख्य .NET CoreFX रेपो में एकीकृत हो जाएगा।

ऐसी संभावना है कि इस कार्यान्वयन को अस्वीकार कर दिया जाएगा।

प्रतिबद्ध कार्यान्वयन को यहां संदर्भित किया जा सकता है https://github.com/dotnet/corefxlab/blob/57be99a176421992e29009701a99a370983329a6/src/Marosoft.Experimental.Collections/Microsoft/Collections/Extr/Oderder/

NuGet पैकेज जो निश्चित रूप से इस प्रकार के उपयोग के लिए उपलब्ध है https://www.nuget.org/packages/Microsoft.Experimental.Collections/1.0.6-e190117-3

या आप Visual Studio के भीतर पैकेज स्थापित कर सकते हैं। पैकेज "Microsoft.Experimental.Collections" के लिए ब्राउज़ करें और सुनिश्चित करें कि "प्रीलेयर शामिल करें" चेकबॉक्स चयनित है।

इस पोस्ट को अपडेट करेगा कि क्या और कब प्रकार आधिकारिक रूप से उपलब्ध कराया गया है।


क्या आप अनुमान लगा सकते हैं कि इसे कब जारी किया जाएगा?
मावेरेसेक

मैं इस पुस्तकालय के विकास में कोई हिस्सा नहीं ले रहा हूं, दुर्भाग्य से मुझे कोई पता नहीं है। यह संभावना है कि यह एक अंतर्निहित ढांचा संग्रह होगा यदि यह कभी भी "अनुमोदित" हो जाता है।
चार्ली

1

मैंने OrderedDictionary<TKey, TValue>चारों ओर से लपेटकर SortedList<TKey, TValue>और एक निजी जोड़कर एक सामान्य लागू किया Dictionary<TKey, int> _order। फिर मैंने Comparer<TKey>_ऑर्डर शब्दकोश के संदर्भ में एक आंतरिक कार्यान्वयन बनाया । तब मैं इस Sorter का उपयोग आंतरिक SortedList के लिए करता हूं। यह वर्ग निर्माणकर्ता को दिए गए तत्वों के क्रम और परिवर्धन के क्रम को बनाए रखता है।

इस कार्यान्वयन में लगभग वैसी ही बड़ी O विशेषताएँ हैं जैसे SortedList<TKey, TValue>_ऑर्डर को जोड़ने और हटाने के लिए O (1) है। प्रत्येक तत्व 22 (ओवरहेड) + 4 (int आदेश) + TKey आकार (चलो मान 8) = 34 के अतिरिक्त मेमोरी स्पेस (पी। 292, तालिका 7-1) के अतिरिक्त मेमोरी स्पेस (पुस्तक 'सी # 4 के अनुसार) लेगा। साथ में SortedList<TKey, TValue>, दो बाइट्स के ओवरहेड के साथ , कुल ओवरहेड 36 बाइट्स है, जबकि एक ही पुस्तक का कहना है कि गैर-जेनेरिक OrderedDictionaryमें 59 बाइट्स का ओवरहेड है।

यदि मैं sorted=trueकंस्ट्रक्टर के पास जाता हूं , तो _ऑर्डर बिल्कुल भी उपयोग नहीं किया जाता है, OrderedDictionary<TKey, TValue>बिल्कुल SortedList<TKey, TValue>रैपिंग के लिए मामूली ओवरहेड के साथ है, अगर सभी अर्थपूर्ण हैं।

मैं इतने बड़े संदर्भ वस्तुओं को स्टोर नहीं करने जा रहा हूं OrderedDictionary<TKey, TValue>, इसलिए मेरे लिए यह सीए। 36 बाइट्स ओवरहेड सहनीय है।

मुख्य कोड नीचे है। पूर्ण अद्यतन कोड इस gist पर है

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
    // ...
}

OrderedDictionaryआवेषण या विलोपन के संबंध में कम से कम चार अलग-अलग उपयोग के मामले हैं , जैसे मैं देख सकता हूं : (1) कभी कोई विलोपन नहीं होगा; (2) वहाँ विलोपन किया जाएगा, लेकिन क्या महत्वपूर्ण है कि आइटम जोड़ा आदेश में enumerate; सूचकांक द्वारा पहुँच की कोई आवश्यकता नहीं है; (3) किसी वस्तु का संख्यात्मक सूचकांक (या कम से कम) स्थिर रहना चाहिए, और संग्रह के जीवनकाल के दौरान 2 बिलियन से अधिक आइटम नहीं जोड़े जाएंगे, इसलिए यदि आइटम # 7 को हटा दिया जाता है, तो फिर कभी नहीं होगा आइटम # 7; (4) किसी वस्तु का सूचकांक जीवित बचे लोगों के संबंध में उसकी रैंक होना चाहिए।
सुपरकैट

1
परिदृश्य # 1 समानांतर में एक सरणी का उपयोग करके नियंत्रित किया जा सकता है Dictionary। परिदृश्य # 2 और # 3 को प्रत्येक आइटम को यह कहते हुए एक सूचकांक बनाए रखने के द्वारा नियंत्रित किया जा सकता है कि इसे कब जोड़ा गया और इसके पहले या बाद में जोड़े गए आइटम के लिंक। परिदृश्य # 4 एकमात्र ऐसा है जो ऐसा प्रतीत होता है कि इसे मनमाना अनुक्रम में संचालन के लिए O (1) प्रदर्शन प्राप्त करने में सक्षम नहीं होना चाहिए। उपयोग के पैटर्न के आधार पर, विभिन्न आलसी अपडेट रणनीतियों का उपयोग करके # 4 की मदद की जा सकती है (एक पेड़ में गिना जाता है, और नोड और उसके माता-पिता को अपडेट करने के बजाय नोड अमान्य में परिवर्तन होते हैं)।
सुपरकाट

1
आंतरिक SortedList में कस्टम तुलनित्र के उपयोग के कारण सम्मिलन क्रम में तत्व हैं। यह धीमा हो सकता है लेकिन गणना के बारे में आपकी टिप्पणी गलत है। गणना के बारे में परीक्षण दिखाएं ...
VB

1
आप किस टौडर के साथ बात कर रहे हैं? यह आंतरिक सूची को प्रभावित नहीं करता है, लेकिन केवल शब्दकोश का आदेश देता है।
VB

1
@ वीबी माफी, मैं दोनों को याद किया। जैसे कि यह परीक्षण नहीं किया है। तब एकमात्र समस्या इसके अतिरिक्त होगी। दो शब्दकोश लुकअप, साथ ही दो सम्मिलन। मैं चढ़ाव को रद्द कर दूंगा।
नवफाल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.