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

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

Dictionary<string, List<object>>

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

Ответы [ 23 ]

214 голосов
/ 28 сентября 2008

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

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

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

155 голосов
/ 09 мая 2009

Класс 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);
}
37 голосов
/ 23 марта 2012

Вот один из способов сделать это с помощью List >

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

21 голосов
/ 28 сентября 2008

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

17 голосов
/ 28 сентября 2008

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

14 голосов
/ 29 сентября 2008

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

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

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

(из MSDN)

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

10 голосов
/ 28 сентября 2008

Я думаю, что-то вроде List<KeyValuePair<object, object>> сделает работу.

8 голосов
/ 17 августа 2012

Если вы используете> = .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());
}
6 голосов
/ 28 сентября 2008

Взгляните на C5 HashBag класс.

5 голосов
/ 07 апреля 2016

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