Дублировать ключи в словарях .NET?

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

Dictionary<string, List<object>>

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

Ответ 1

Если вы используете .NET 3.5, используйте класс Lookup.

EDIT: обычно вы создаете Lookup с помощью Enumerable.ToLookup. Это предполагает, что вам не нужно менять его потом, но я обычно считаю это достаточно хорошим.

Если это не сработает для вас, я не думаю, что что-то в рамках, которое поможет - и использование словаря так же хорошо, как и получается: (

Ответ 2

Класс List действительно хорошо работает для коллекций ключей/значений, содержащих дубликаты, в которых вы хотите перебирать коллекцию. Пример:

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

Ответ 3

Вот один из способов сделать это с помощью List < KeyValuePair < строка, строкa →

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

Ответ 4

Если вы используете строки как ключи и значения, вы можете использовать System.Collections.Specialized.NameValueCollection, который вернет массив строковых значений с помощью метода GetValues ​​(строковый ключ).

Ответ 5

Я просто наткнулся на библиотеку PowerCollections, которая включает, помимо прочего, класс MultiDictionary. Это аккуратно обертывает этот тип функциональности.

Ответ 6

Очень важное примечание относительно использования Lookup:

Вы можете создать экземпляр Lookup(TKey, TElement), вызвав ToLookup для объекта, который реализует IEnumerable(T)

Нет открытого конструктора для создания нового экземпляра Lookup(TKey, TElement). Кроме того, объекты Lookup(TKey, TElement) неизменяемы, то есть вы не можете добавлять или удалять элементы или ключи из объекта Lookup(TKey, TElement) после его создания.

(из MSDN)

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

Ответ 7

Я думаю, что что-то вроде List<KeyValuePair<object, object>> будет выполнять Job.

Ответ 8

Если вы используете > =.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());
}

Ответ 9

Посмотрите C5 HashBag class.

Ответ 10

В ответ на исходный вопрос. Что-то вроде Dictionary<string, List<object>> реализуется в классе MultiMap в Code Project.

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

Ответ 11

Это достаточно просто, чтобы "сворачивать свою собственную" версию словаря, которая допускает записи "дублировать ключ". Вот грубая простая реализация. Возможно, вы захотите рассмотреть возможность добавления поддержки для большинства (если не всех) в 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));
}

Ответ 12

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

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

Ответ 13

При использовании параметра 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 }

Ответ 14

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

Dictionary<string, List<string>>

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

Пример:

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

Ответ 15

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

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

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

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

Если вам разрешено вставлять повторяющиеся ключи, вы не сможете отличить, какие ключи сопоставляются с какими значениями.

Ответ 16

Я наткнулся на это сообщение в поисках одного и того же ответа и не нашел ни одного, поэтому я придумал пример с одним голосом, используя список словарей, переопределив оператор [], чтобы добавить новый словарь в список, когда все другие имеют заданный ключ (набор) и возвращают список значений (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;
            }
        }
    }

Ответ 17

Я изменил ответ @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();
    }
  }

Ответ 18

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

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
            }

Ответ 19

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

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;

Ответ 20

U может определить способ построения сложного строкового ключа каждый раз, когда вы хотите использовать словарь u, необходимо использовать этот метод для создания вашего ключа например:

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

для использования:

myDict.ContainsKey(keyBuilder(key1, key2))

Ответ 21

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

Ответ 22

Также это возможно:

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

Таким образом, у нас могут быть уникальные ключи. Надеюсь, это сработает для вас.

Ответ 23

Вы можете добавить те же клавиши с другим случаем, как:

key1
Key1
KEY1
KeY1
kEy1
keY1

Я знаю, фиктивный ответ, но работал на меня.