Реализация словаря, в котором эквивалентное содержимое равно и возвращает один и тот же хэш-код независимо от порядка вставки

Мне нужно использовать Dictionary коллекции, в которых даны два экземпляра d1 и d2 , где каждый из них имеет одинаковое KeyValuePair содержимое, которое может быть вставлено в любом порядке:

  1. (d1 == d2) оценивается как true
  2. d1.GetHashCode () == d2.GetHashCode ()

Первое требование было легче всего выполнить с помощью SortedDictionar y вместо обычного Dictionary .

Второе требование необходимо, потому что у меня есть одна точка, где мне нужно сохранить Dictionary , List - основной тип Dictionary используется в качестве ключа для другого Dictionary , и если HashCodes не оцениваются на основе идентичного содержимого, использование ContainsKey () не будет работать так, как я хочу (то есть: если в словарь уже вставлен элемент с ключом d1 , то dictionary.ContainsKey (d2) должен оценивается как true .

Для этого я создал новый объект class ComparableDictionary: SortedDictionary и включил следующее:

public override int GetHashCode() {            
   StringBuilder str = new StringBuilder();
   foreach (var item in this) {
      str.Append(item.Key);
      str.Append("_");
      str.Append(item.Value);
      str.Append("%%");
   }
   return str.ToString().GetHashCode();
 }

В мои модульное тестирование, это соответствует критериям равенства и хэш-кодов. Однако, читая Рекомендации и правила для GetHashCode , я столкнулся со следующим:

Правило: целое число, возвращаемое GetHashCode, никогда не должно изменяться, пока объект содержится в структуре данных, которая зависит от хэш-кода. Остается стабильным

Допустимо, хотя и опасно, создавать объект, значение хэш-кода которого может изменяться по мере изменения полей объекта. Если у вас есть такой объект и вы помещаете его в хеш-таблицу, тогда код, который изменяет объект, и код, который поддерживает хеш-таблицу, должны иметь какой-то согласованный протокол, который гарантирует, что объект не будет мутировать, пока он находится в хеш-таблица. Как будет выглядеть этот протокол, зависит от вас.

Если хэш-код объекта может изменяться, пока он находится в хеш-таблице, очевидно, что метод Contains перестает работать. Вы помещаете объект в корзину № 5, изменяете его, и когда вы спрашиваете набор, содержит ли он измененный объект, он смотрит в корзину № 74 и не находит его.

Помните, объекты могут быть помещены в хэш таблицы способами, которых вы не ожидали. Многие операторы последовательности LINQ внутренне используют хэш-таблицы. Не подвергайте опасные мутации объекты при перечислении LINQ-запроса, который их возвращает!

Теперь Dictionary > используется только один раз в коде, в месте, где содержимое из всех коллекций ComparableDictionary должны быть установлены. Таким образом, согласно этим рекомендациям, я думаю , что было бы приемлемо переопределить GetHashCode , как это сделал я (полностью основывая его на содержимом словаря).

После этого введения мои вопросы следующие:

  1. Я знаю, что производительность SortedDictionary очень низка по сравнению с Dictionary (а у меня могут быть сотни экземпляров объекта). Единственная причина использования SortedDictionary заключается в том, что я могу выполнять сравнение на равенство на основе содержимого словаря, независимо от порядка вставки. Есть ли лучший способ выполнить это требование равенства без использования SortedDictionary ?
  2. Является ли моя реализация GetHashCode приемлемой с учетом требований? Несмотря на то, что он основан на изменяемом содержимом, я не думаю, что это должно представлять какой-либо риск, поскольку единственное место, где он используется (я думаю), - это после того, как содержимое было установлено.

Примечание : while Я настраивал их с помощью Dictionary или SortedDictionary , я не привязан к этим типам коллекций. Основная потребность - это коллекция, которая может хранить пары значений и удовлетворять требованиям равенства и хеширования, определенным выше.

6
задан Yaakov Ellis 29 May 2011 в 14:28
поделиться