سؤال

هل توجد أي فئات قاموس في مكتبة الفئة الأساسية .NET تسمح باستخدام المفاتيح المكررة؟الحل الوحيد الذي وجدته هو إنشاء فئة مثل: على سبيل المثال:

Dictionary<string, List<object>>

ولكن هذا أمر مزعج للغاية للاستخدام الفعلي.في Java، أعتقد أن MultiMap ينجز هذا، لكن لا يمكن العثور على نظير في .NET.

هل كانت مفيدة؟

المحلول

إذا كنت تستخدم ‎.NET 3.5، فاستخدم Lookup فصل.

يحرر:بشكل عام تقوم بإنشاء ملف Lookup استخدام Enumerable.ToLookup.يفترض هذا أنك لا تحتاج إلى تغييره بعد ذلك - لكنني عادة أجد أن هذا جيد بما فيه الكفاية.

لو كان ذلك لا يناسبك، لا أعتقد أن هناك أي شيء في الإطار سيساعدك - واستخدام القاموس هو أفضل ما يمكن الحصول عليه :(

نصائح أخرى

تعمل فئة القائمة بشكل جيد جدًا مع مجموعات المفاتيح/القيم التي تحتوي على نسخ مكررة حيث ترغب في تكرارها على المجموعة.مثال:

List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();

// add some values to the collection here

for (int i = 0;  i < list.Count;  i++)
{
    Print(list[i].Key, list[i].Value);
}

إليك إحدى الطرق للقيام بذلك باستخدام List< KeyValuePair< string, string > >

public class ListWithDuplicates : List<KeyValuePair<string, string>>
{
    public void Add(string key, string value)
    {
        var element = new KeyValuePair<string, string>(key, value);
        this.Add(element);
    }
}

var list = new ListWithDuplicates();
list.Add("k1", "v1");
list.Add("k1", "v2");
list.Add("k1", "v3");

foreach(var item in list)
{
    string x = string.format("{0}={1}, ", item.Key, item.Value);
}

المخرجات k1=v1، k1=v2، k1=v3

إذا كنت تستخدم سلاسل كمفاتيح وقيم، فيمكنك استخدامها System.Collections.Specialized.NameValueCollection, ، والذي سيُرجع مصفوفة من قيم السلسلة عبر طريقة GetValues(string key).

لقد صادفت للتو مجموعات الطاقة المكتبة التي تتضمن، من بين أمور أخرى، فئة تسمى MultiDictionary.هذا يلتف بدقة هذا النوع من الوظائف.

ملاحظة مهمة جدًا بخصوص استخدام Lookup:

يمكنك إنشاء مثيل لـ Lookup(TKey, TElement) بالاتصال ToLookup على كائن ينفذ IEnumerable(T)

لا يوجد منشئ عام لإنشاء مثيل جديد لـ a Lookup(TKey, TElement).بالإضافة إلى ذلك، Lookup(TKey, TElement) الكائنات غير قابلة للتغيير، أي أنه لا يمكنك إضافة أو إزالة عناصر أو مفاتيح من ملف Lookup(TKey, TElement) الكائن بعد إنشائه.

(من MSDN)

أعتقد أن هذا سيكون بمثابة سدادة عرض لمعظم الاستخدامات.

أعتقد شيئا من هذا القبيل List<KeyValuePair<object, object>> سوف تقوم بهذه المهمة.

إذا كنت تستخدم >= .NET 4، فيمكنك استخدامه Tuple فصل:

// declaration
var list = new List<Tuple<string, List<object>>>();

// to add an item to the list
var item = Tuple<string, List<object>>("key", new List<object>);
list.Add(item);

// to iterate
foreach(var i in list)
{
    Console.WriteLine(i.Item1.ToString());
}

القي نظرة على C5 HashBag فصل.

من السهل جدًا "إنشاء إصدار خاص بك" من القاموس الذي يسمح بإدخالات "مفتاح مكرر".هنا تنفيذ بسيط تقريبي.قد ترغب في التفكير في إضافة دعم لمعظم (إن لم يكن جميعها) بشكل أساسي IDictionary<T>.

public class MultiMap<TKey,TValue>
{
    private readonly Dictionary<TKey,IList<TValue>> storage;

    public MultiMap()
    {
        storage = new Dictionary<TKey,IList<TValue>>();
    }

    public void Add(TKey key, TValue value)
    {
        if (!storage.ContainsKey(key)) storage.Add(key, new List<TValue>());
        storage[key].Add(value);
    }

    public IEnumerable<TKey> Keys
    {
        get { return storage.Keys; }
    }

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

    public IList<TValue> this[TKey key]
    {
        get
        {
            if (!storage.ContainsKey(key))
                throw new KeyNotFoundException(
                    string.Format(
                        "The given key {0} was not found in the collection.", key));
            return storage[key];
        }
    }
}

مثال سريع عن كيفية استخدامه:

const string key = "supported_encodings";
var map = new MultiMap<string,Encoding>();
map.Add(key, Encoding.ASCII);
map.Add(key, Encoding.UTF8);
map.Add(key, Encoding.Unicode);

foreach (var existingKey in map.Keys)
{
    var values = map[existingKey];
    Console.WriteLine(string.Join(",", values));
}

ردا على السؤال الأصلي.شيء مثل Dictionary<string, List<object>> يتم تنفيذه في فئة تسمى MultiMap في ال Code Project.

يمكنك العثور على مزيد من المعلومات على الرابط أدناه:http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

يدعم NameValueCollection قيم سلسلة متعددة ضمن مفتاح واحد (وهو أيضًا سلسلة)، ولكنه المثال الوحيد الذي أعرفه.

أميل إلى إنشاء بنيات مشابهة لتلك الموجودة في مثالك عندما أواجه مواقف أحتاج فيها إلى هذا النوع من الوظائف.

عند استخدام List<KeyValuePair<string, object>> الخيار، يمكنك استخدام LINQ لإجراء البحث:

List<KeyValuePair<string, object>> myList = new List<KeyValuePair<string, object>>();
//fill it here
var q = from a in myList Where a.Key.Equals("somevalue") Select a.Value
if(q.Count() > 0){ //you've got your value }

الطريقة التي أستخدمها هي فقط

Dictionary<string, List<string>>

بهذه الطريقة يكون لديك مفتاح واحد يحمل قائمة من السلاسل.

مثال:

List<string> value = new List<string>();
if (dictionary.Contains(key)) {
     value = dictionary[key];
}
value.Add(newValue);

هل تقصد متطابقة وليست مكررة فعلية؟وإلا فلن يتمكن جدول التجزئة من العمل.

التطابق يعني أنه يمكن تجزئة مفتاحين منفصلين إلى قيمة مكافئة، لكن المفاتيح ليست متساوية.

على سبيل المثال:لنفترض أن وظيفة التجزئة الخاصة بجدول التجزئة الخاص بك كانت مجرد hashval = key mod 3.كل من 1 و 4 يقابلان 1، لكنهما قيمتان مختلفتان.هذا هو المكان الذي تلعب فيه فكرة القائمة الخاصة بك.

عندما تحتاج إلى البحث عن 1، يتم تجزئة هذه القيمة إلى 1، ويتم اجتياز القائمة حتى يتم العثور على المفتاح = 1.

إذا سمحت بإدراج مفاتيح مكررة، فلن تتمكن من التمييز بين المفاتيح التي ترتبط بالقيم.

لقد عثرت على هذا المنشور بحثًا عن نفس الإجابة، ولم أجد أيًا منها، لذلك قمت بإعداد مثال حل بسيط باستخدام قائمة القواميس، متجاوزًا عامل التشغيل [] لإضافة قاموس جديد إلى القائمة عندما يكون لدى جميع الآخرين مفتاح معين (مجموعة)، وإرجاع قائمة القيم (الحصول على).
إنه قبيح وغير فعال، فهو يحصل/يضبط فقط حسب المفتاح، ويعيد دائمًا قائمة، لكنه يعمل:

 class DKD {
        List<Dictionary<string, string>> dictionaries;
        public DKD(){
            dictionaries = new List<Dictionary<string, string>>();}
        public object this[string key]{
             get{
                string temp;
                List<string> valueList = new List<string>();
                for (int i = 0; i < dictionaries.Count; i++){
                    dictionaries[i].TryGetValue(key, out temp);
                    if (temp == key){
                        valueList.Add(temp);}}
                return valueList;}
            set{
                for (int i = 0; i < dictionaries.Count; i++){
                    if (dictionaries[i].ContainsKey(key)){
                        continue;}
                    else{
                        dictionaries[i].Add(key,(string) value);
                        return;}}
                dictionaries.Add(new Dictionary<string, string>());
                dictionaries.Last()[key] =(string)value;
            }
        }
    }

لقد قمت بتغيير إجابة @Hector Correa إلى امتداد بأنواع عامة وأضفت أيضًا TryGetValue مخصصة إليها.

  public static class ListWithDuplicateExtensions
  {
    public static void Add<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, TValue value)
    {
      var element = new KeyValuePair<TKey, TValue>(key, value);
      collection.Add(element);
    }

    public static int TryGetValue<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, out IEnumerable<TValue> values)
    {
      values = collection.Where(pair => pair.Key.Equals(key)).Select(pair => pair.Value);
      return values.Count();
    }
  }

هذه طريقة سحب للقاموس المتزامن وأعتقد أن هذا سيساعدك:

public class HashMapDictionary<T1, T2> : System.Collections.IEnumerable
{
    private System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>> _keyValue = new System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>>();
    private System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>> _valueKey = new System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>>();

    public ICollection<T1> Keys
    {
        get
        {
            return _keyValue.Keys;
        }
    }

    public ICollection<T2> Values
    {
        get
        {
            return _valueKey.Keys;
        }
    }

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

    public bool IsReadOnly
    {
        get
        {
            return false;
        }
    }

    public List<T2> this[T1 index]
    {
        get { return _keyValue[index]; }
        set { _keyValue[index] = value; }
    }

    public List<T1> this[T2 index]
    {
        get { return _valueKey[index]; }
        set { _valueKey[index] = value; }
    }

    public void Add(T1 key, T2 value)
    {
        lock (this)
        {
            if (!_keyValue.TryGetValue(key, out List<T2> result))
                _keyValue.TryAdd(key, new List<T2>() { value });
            else if (!result.Contains(value))
                result.Add(value);

            if (!_valueKey.TryGetValue(value, out List<T1> result2))
                _valueKey.TryAdd(value, new List<T1>() { key });
            else if (!result2.Contains(key))
                result2.Add(key);
        }
    }

    public bool TryGetValues(T1 key, out List<T2> value)
    {
        return _keyValue.TryGetValue(key, out value);
    }

    public bool TryGetKeys(T2 value, out List<T1> key)
    {
        return _valueKey.TryGetValue(value, out key);
    }

    public bool ContainsKey(T1 key)
    {
        return _keyValue.ContainsKey(key);
    }

    public bool ContainsValue(T2 value)
    {
        return _valueKey.ContainsKey(value);
    }

    public void Remove(T1 key)
    {
        lock (this)
        {
            if (_keyValue.TryRemove(key, out List<T2> values))
            {
                foreach (var item in values)
                {
                    var remove2 = _valueKey.TryRemove(item, out List<T1> keys);
                }
            }
        }
    }

    public void Remove(T2 value)
    {
        lock (this)
        {
            if (_valueKey.TryRemove(value, out List<T1> keys))
            {
                foreach (var item in keys)
                {
                    var remove2 = _keyValue.TryRemove(item, out List<T2> values);
                }
            }
        }
    }

    public void Clear()
    {
        _keyValue.Clear();
        _valueKey.Clear();
    }

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

أمثلة:

public class TestA
{
    public int MyProperty { get; set; }
}

public class TestB
{
    public int MyProperty { get; set; }
}

            HashMapDictionary<TestA, TestB> hashMapDictionary = new HashMapDictionary<TestA, TestB>();

            var a = new TestA() { MyProperty = 9999 };
            var b = new TestB() { MyProperty = 60 };
            var b2 = new TestB() { MyProperty = 5 };
            hashMapDictionary.Add(a, b);
            hashMapDictionary.Add(a, b2);
            hashMapDictionary.TryGetValues(a, out List<TestB> result);
            foreach (var item in result)
            {
                //do something
            }

أستخدم هذه الفئة البسيطة:

public class ListMap<T,V> : List<KeyValuePair<T, V>>
{
    public void Add(T key, V value) {
        Add(new KeyValuePair<T, V>(key, value));
    }

    public List<V> Get(T key) {
        return FindAll(p => p.Key.Equals(key)).ConvertAll(p=> p.Value);
    }
}

الاستخدام:

var fruits = new ListMap<int, string>();
fruits.Add(1, "apple");
fruits.Add(1, "orange");
var c = fruits.Get(1).Count; //c = 2;

يمكنك تحديد طريقة لبناء مفتاح سلسلة مركب في كل مكان تريد فيه استخدام القاموس الذي يجب أن تستخدم هذه الطريقة لإنشاء مفتاحك على سبيل المثال:

private string keyBuilder(int key1, int key2)
{
    return string.Format("{0}/{1}", key1, key2);
}

لاستخدام:

myDict.ContainsKey(keyBuilder(key1, key2))

المفاتيح المكررة تنتهك عقد القاموس بأكمله.في القاموس، يكون كل مفتاح فريدًا ويتم تعيينه لقيمة واحدة.إذا كنت تريد ربط كائن بعدد عشوائي من الكائنات الإضافية، فقد يكون أفضل خيار هو شيء يشبه DataSet (في اللغة الشائعة جدول).ضع مفاتيحك في عمود واحد وقيمك في العمود الآخر.يعد هذا أبطأ بكثير من القاموس، ولكن هذه هي مقايضتك لفقد القدرة على تجزئة الكائنات الرئيسية.

وهذا ممكن أيضًا:

Dictionary<string, string[]> previousAnswers = null;

بهذه الطريقة، يمكننا الحصول على مفاتيح فريدة.أتمنى أن يكون هذا مناسبا لك.

يمكنك إضافة نفس المفاتيح مع حالة مختلفة مثل:

مفتاح1
المفتاح1
المفتاح1
مفتاح1
مفتاح1
مفتاح1

أعلم أن الإجابة وهمية، لكنها عملت بالنسبة لي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top