Вопрос

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

Dictionary<string, List<object>>

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

Это было полезно?

Решение

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

РЕДАКТИРОВАТЬ:Обычно вы создаете Lookup с использованием Enumerable.ToLookup.Это предполагает, что вам не нужно будет менять его впоследствии, но обычно я считаю, что этого достаточно.

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

Другие советы

Класс 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);
}

Вот один из способов сделать это с помощью 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).

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

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

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

Не существует общедоступного конструктора для создания нового экземпляра 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());
}

Посмотри на С5 ХэшБэг сорт.

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

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

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

Я изменил ответ @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
ключY1
ключ1
ключ1

Я знаю, что это пустышка, но у меня сработало.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top