Словарь C#: каждый ключ имеет идентичное значение — можно ли удалить избыточность?

Рассмотрим следующий код, где каждый ключ имеет одинаковое значение:

IDictionary<string, string> quarterbackDictionary = new Dictionary<string, string>();
quarterbackDictionary.Add("Manning", "Manning");
quarterbackDictionary.Add("Brady", "Brady");
quarterbackDictionary.Add("Rivers", "Rivers");

Мой вопрос:

  • Могу ли я удалить избыточность, чтобы мне не приходилось дважды повторять каждую строку, как показано ниже:
IDictionary<string, string> quarterbackDictionary = new Dictionary<string, string>();
quarterbackDictionary.Add("Manning");
quarterbackDictionary.Add("Brady");
quarterbackDictionary.Add("Rivers");

К вашему сведению:

  • Я использую словарь, потому что хочу бросить попытку вставить дубликат ключа.
  • HashSet не выдаст ошибку при попытке вставить дубликат ключа.

person Jim G.    schedule 03.12.2009    source источник
comment
Почему должен key == value? Почему бы не использовать d.Add("Manning", null) или HashSet, как предлагает driis?   -  person dalle    schedule 03.12.2009


Ответы (5)


Возможно, вы могли бы использовать метод расширения

public static class DictionaryExtensions
{
    public static void Add(this Dictionary<string, string> dictionary,  
        string keyAndValue)
    {
        string value;
        if (dictionary.TryGetValue(keyAndValue, out value))
        {
            throw new Exception();
        }

        dictionary.Add(keyAndValue, keyAndValue);
    }
}
person mrydengren    schedule 03.12.2009
comment
Я думаю, что метод расширения - это путь, но вместо этого я бы добавил его в HashSet. - person philsquared; 03.12.2009
comment
Я, вероятно, предпочел бы добавить метод расширения в HashSet или реализовать свою собственную коллекцию, как это сделал driis. Но автор темы, похоже, решил использовать словарь ‹, именно так я решил использовать его для своего ответа. - person mrydengren; 03.12.2009
comment
ОП (Джим) заявил: я использую словарь, потому что хочу бросить попытку вставить дубликат ключа. Если это единственная причина, я думаю, что HashSet подойдет лучше. - person philsquared; 03.12.2009
comment
@Phil Nash: Hashset сработал бы тоже. Шесть одних или полдюжины других. Я тоже дал вашему ответу +1. - person Jim G.; 03.12.2009
comment
Спасибо, @Jim G. Однако я бы оценил его как 5 из одного 7 из другого ;-) Если HashSet подходит, то его преимущество заключается в меньших накладных расходах на хранение (словарь будет хранить каждый элемент дважды - один раз для ключ один раз для значения). Концептуально это вдвое больше места для хранения — хотя на практике оно будет меньше из-за хеширования и других накладных расходов — но, вероятно, в 1,5 раза больше. - person philsquared; 04.12.2009

Вы можете обернуть строку HashSet‹ в свой собственный класс и пусть он выдаст исключение, если вы попытаетесь добавить один и тот же ключ дважды.

На самом деле определить этот класс не составит труда, вот возможная реализация, которую вы можете настроить в соответствии со своими потребностями:

    public class UniqueHashSet<T> : ICollection<T>
    {
        private readonly HashSet<T> innerSet = new HashSet<T>();

        public void Add(T item)
        {
            if (innerSet.Contains(item))
                throw new ArgumentException("Element already exists", "item");
            innerSet.Add(item);
        }

        public void Clear()
        {
            innerSet.Clear();
        }

        public bool Contains(T item)
        {
            return innerSet.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            innerSet.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            return innerSet.Remove(item);
        }

        public int Count
        {
            get { return innerSet.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return innerSet.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return innerSet.GetEnumerator();
        }
    }

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

person driis    schedule 03.12.2009
comment
-1: я использую словарь, потому что хочу бросить попытку вставить дубликат ключа. - person Jim G.; 03.12.2009
comment
Вместо использования Dictionary‹› почему бы вам не использовать HashSet‹› и добавить функцию, которая выдает исключение, если ключ уже существует, или добавляет ключ к хэшу? - person Aaron; 03.12.2009
comment
@ Аарон: именно то, о чем я думал. Если вам НЕОБХОДИМО исключение, используйте функцию (расширение?), где if (Add == false) throw new Exception(); - person Will Eddins; 03.12.2009
comment
d'oh - вы, ребята, пришли к тому же выводу, что и я, пока я печатал свой - person philsquared; 03.12.2009

Добавьте в HashSet метод расширения, скажем, AddUnique, который просто вызывает Add и выдает исключение, если возвращается false.

person philsquared    schedule 03.12.2009

Наследовать от System.Collections.ObjectModel.Collection и переопределить InsertItem (который защищен).

Затем вы можете выполнить проверку дубликатов и выбросить, когда кто-то вставит повторяющийся элемент. InsertItem вызывается для любого из методов, которые могут поместить новый элемент: Add, Insert и т. д.

person Ryan Lundy    schedule 03.12.2009
comment
Класс System.Collections.ObjectModel.Collection по-прежнему является коллекцией на основе массива; поиск предметов в нем с помощью .Contains() займет линейное время. Преимущество словаря в том, что время поиска примерно постоянно. - person Kevin Kibler; 03.12.2009
comment
Было бы интересно увидеть приложение настолько сильно оптимизированным, что это действительно имеет значение. - person Ryan Lundy; 03.12.2009

Вы также можете наследовать от System.Collections.ObjectModel.KeyedCollection.

class MyDictionary : KeyedCollection<string, string>
{
    protected override string GetKeyForItem(string item)
    {
        return item;
    }
}

var d = new MyDictionary();
d.Add("jones");
d.Add("jones");   // this will except
person dkackman    schedule 03.12.2009
comment
+1 KeyedCollection можно использовать в .NET 2.0. Hashset доступен только в .NET 3.5. - person Kevin Kibler; 03.12.2009