Дублирующиеся ключи в словарях .NET? - PullRequest
238 голосов
/ 28 сентября 2008

Есть ли в библиотеке базовых классов .NET какие-либо словарные классы, позволяющие использовать дублирующиеся ключи? Единственное решение, которое я нашел, - это создать, например, такой класс:

Dictionary<string, List<object>>

Но это довольно раздражает на самом деле использовать. Я полагаю, что в Java MultiMap выполняет это, но не может найти аналог в .NET.

Ответы [ 23 ]

4 голосов
/ 28 мая 2010

В ответ на оригинальный вопрос. Нечто подобное Dictionary<string, List<object>> реализовано в классе под названием MultiMap в Code Project.

Вы можете найти больше информации по ссылке ниже: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

3 голосов
/ 19 июля 2011

При использовании опции 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 }
3 голосов
/ 28 сентября 2008

NameValueCollection поддерживает несколько строковых значений под одним ключом (который также является строкой), но это единственный известный мне пример.

Я склонен создавать конструкции, подобные той, что в вашем примере, когда я сталкиваюсь с ситуациями, когда мне нужна такая функциональность.

2 голосов
/ 07 апреля 2012

Я использую только

Dictionary<string, List<string>>

Таким образом, у вас есть одна клавиша, содержащая список строк.

Пример:

List<string> value = new List<string>();
if (dictionary.Contains(key)) {
     value = dictionary[key];
}
value.Add(newValue);
1 голос
/ 28 сентября 2008

Вы имеете в виду конгруэнтный, а не реальный дубликат? В противном случае хеш-таблица не сможет работать.

Конгруэнтный означает, что два отдельных ключа могут хэшировать до эквивалентного значения, но ключи не равны.

Например: скажем, хеш-функция вашей хеш-таблицы была просто hashval = key mod 3. И 1, и 4 отображаются на 1, но это разные значения. Вот где ваша идея списка вступает в игру.

Когда вам нужно искать 1, это значение хэшируется до 1, список просматривается до тех пор, пока не будет найден ключ = 1.

Если вы разрешите вставку дубликатов ключей, вы не сможете различить, какие ключи соответствуют каким значениям.

1 голос
/ 23 мая 2011

Я наткнулся на этот пост в поисках того же ответа и не нашел ни одного, поэтому я подготовил примерное решение с использованием списка словарей, переопределив оператор [], чтобы добавить новый словарь в список, когда другие имеют заданный ключ (set) и возвращают список значений (get).
Это уродливо и неэффективно, ТОЛЬКО получает / устанавливает по ключу, и всегда возвращает список, но работает:

 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;
            }
        }
    }
1 голос
/ 30 апреля 2018

Я изменил ответ @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();
    }
  }
0 голосов
/ 17 мая 2019

я использую этот простой класс:

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;
0 голосов
/ 28 ноября 2017

Это параллельный словарь для буксировки. Я думаю, это поможет вам:

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
            }
0 голосов
/ 28 сентября 2008

Дублирующиеся ключи нарушают весь контракт словаря. В словаре каждый ключ уникален и сопоставлен с одним значением. Если вы хотите связать объект с произвольным числом дополнительных объектов, лучшим вариантом может быть что-то похожее на DataSet (в общем на языке таблицы). Поместите свои ключи в один столбец, а свои значения - в другой. Это значительно медленнее, чем словарь, но это ваш компромисс за потерю способности хешировать ключевые объекты.

...