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

243
задан Mechanical snail 7 November 2012 в 18:48
поделиться

12 ответов

При использовании.NET 3.5 используйте Lookup класс.

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

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

221
ответ дан Jon Skeet 23 November 2019 в 03:10
поделиться

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

-3
ответ дан Ryan 23 November 2019 в 03:10
поделиться

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

6
ответ дан Yuval 23 November 2019 в 03:10
поделиться

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

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

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

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

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

1
ответ дан Nicholas Mancuso 23 November 2019 в 03:10
поделиться

Я думаю, что что-то как List<KeyValuePair<object, object>> сделало бы Задание.

10
ответ дан MADMap 23 November 2019 в 03:10
поделиться

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

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

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

(из MSDN)

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

14
ответ дан Adrian Carneiro 23 November 2019 в 03:10
поделиться

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

18
ответ дан 23 November 2019 в 03:10
поделиться

Если Вы используете строки и в качестве ключей и в качестве значений, можно использовать Система. Наборы. Специализированный. NameValueCollection, который возвратит массив строковых значений через GetValues (строковый ключ) метод.

21
ответ дан Matt 23 November 2019 в 03:10
поделиться

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

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

3
ответ дан ckramer 23 November 2019 в 03:10
поделиться

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

/// <summary>
/// Dictionary which supports duplicates and null entries
/// </summary>
/// <typeparam name="TKey">Type of key</typeparam>
/// <typeparam name="TValue">Type of items</typeparam>
public class OpenDictionary<TKey, TValue>
{
    private readonly Lazy<List<TValue>> _nullStorage = new Lazy<List<TValue>>(
        () => new List<TValue>());

    private readonly Dictionary<TKey, List<TValue>> _innerDictionary =
        new Dictionary<TKey, List<TValue>>();

    /// <summary>
    /// Get all entries
    /// </summary>
    public IEnumerable<TValue> Values =>
        _innerDictionary.Values
            .SelectMany(x => x)
            .Concat(_nullStorage.Value);

    /// <summary>
    /// Add an item
    /// </summary>
    public OpenDictionary<TKey, TValue> Add(TKey key, TValue item)
    {
        if (ReferenceEquals(key, null))
            _nullStorage.Value.Add(item);
        else
        {
            if (!_innerDictionary.ContainsKey(key))
                _innerDictionary.Add(key, new List<TValue>());

            _innerDictionary[key].Add(item);
        }

        return this;
    }

    /// <summary>
    /// Remove an entry by key
    /// </summary>
    public OpenDictionary<TKey, TValue> RemoveEntryByKey(TKey key, TValue entry)
    {
        if (ReferenceEquals(key, null))
        {
            int targetIdx = _nullStorage.Value.FindIndex(x => x.Equals(entry));
            if (targetIdx < 0)
                return this;

            _nullStorage.Value.RemoveAt(targetIdx);
        }
        else
        {
            if (!_innerDictionary.ContainsKey(key))
                return this;

            List<TValue> targetChain = _innerDictionary[key];
            if (targetChain.Count == 0)
                return this;

            int targetIdx = targetChain.FindIndex(x => x.Equals(entry));
            if (targetIdx < 0)
                return this;

            targetChain.RemoveAt(targetIdx);
        }

        return this;
    }

    /// <summary>
    /// Remove all entries by key
    /// </summary>
    public OpenDictionary<TKey, TValue> RemoveAllEntriesByKey(TKey key)
    {
        if (ReferenceEquals(key, null))
        {
            if (_nullStorage.IsValueCreated)
                _nullStorage.Value.Clear();
        }       
        else
        {
            if (_innerDictionary.ContainsKey(key))
                _innerDictionary[key].Clear();
        }

        return this;
    }

    /// <summary>
    /// Try get entries by key
    /// </summary>
    public bool TryGetEntries(TKey key, out IReadOnlyList<TValue> entries)
    {
        entries = null;

        if (ReferenceEquals(key, null))
        {
            if (_nullStorage.IsValueCreated)
            {
                entries = _nullStorage.Value;
                return true;
            }
            else return false;
        }
        else
        {
            if (_innerDictionary.ContainsKey(key))
            {
                entries = _innerDictionary[key];
                return true;
            }
            else return false;
        }
    }
}

образец использования:

var dictionary = new OpenDictionary<string, int>();
dictionary.Add("1", 1); 
// The next line won't throw an exception; 
dictionary.Add("1", 2);

dictionary.TryGetEntries("1", out List<int> result); 
// result is { 1, 2 }

dictionary.Add(null, 42);
dictionary.Add(null, 24);
dictionary.TryGetEntries(null, out List<int> result); 
// result is { 42, 24 }
0
ответ дан 23 November 2019 в 03:10
поделиться

Класс 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);
}
160
ответ дан 23 November 2019 в 03:10
поделиться

В ответ на исходный вопрос. Что-то вроде Dictionary> реализовано в классе под названием MultiMap в The Code Project.

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

4
ответ дан 23 November 2019 в 03:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: