Как реализован словарь c # /. Net 3.5?

Я использую приложение, которое использует несколько больших словарей (до 10 ^ 6 элементов), размер которых заранее неизвестен (хотя я могу догадаться в некоторых случаях ). Мне интересно, как реализован словарь, то есть, насколько плох, если я не даю первоначальную оценку размера словаря. Использует ли он внутренне (саморазвивающийся) массив так, как это делает List? в этом случае расширение словарей может привести к появлению большого количества массивов без ссылок на LOH.

42
задан willem 19 August 2010 в 12:03
поделиться

4 ответа

Используя Reflector, я обнаружил следующее: Словарь хранит данные в массиве struct. Он ведет подсчет того, сколько пустых мест осталось в этом массиве. Когда вы добавляете элемент и пустых мест не остается, он увеличивает размер внутреннего массива (см. ниже) и копирует данные из старого массива в новый.

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

EDIT: Логика на самом деле довольно интересная: Существует внутренний класс под названием HashHelpers для поиска простых чисел. Чтобы ускорить этот процесс, он также хранит некоторые простые числа в статическом массиве от 3 до 7199369 (некоторые отсутствуют; причину см. ниже). Когда вы задаете емкость, он находит следующее простое число (то же или большее) из массива и использует его в качестве начальной емкости. Если вы даете ему большее число, чем в его массиве, он начинает проверку вручную.

Таким образом, если Словарю ничего не передано в качестве емкости, начальная емкость равна трем.

Как только емкость превышена, он умножает текущую емкость на два, а затем находит следующее большее число, используя класс-помощник. Именно поэтому в массиве не все простые нужны, так как простые "слишком близко друг к другу" на самом деле не нужны.

Итак, если мы не передадим никакого начального значения, мы получим (я проверил внутренний массив):

  1. 3
  2. 7
  3. 17
  4. 37
  5. 71
  6. 163
  7. 353
  8. 761
  9. 1597
  10. 3371
  11. 7013
  12. 14591
  13. 30293
  14. 62851
  15. 130363
  16. 270371
  17. 560689
  18. 1162687
  19. 2411033
  20. 4999559

Once мы проходим этот размер, следующий шаг выходит за пределы внутреннего массива, и он будет вручную искать более крупные простые числа. Это будет довольно медленно. Вы можете инициализировать с 7199369 (самое большое значение в массиве), или подумать, не означает ли наличие более 5 миллионов записей в словаре, что вам следует пересмотреть свой дизайн.

70
ответ дан 26 November 2019 в 23:43
поделиться

Лучшим способом для меня было бы использовать .NET Reflector.

http://www.red-gate.com/products/reflector/

Используйте дизассемблированный код, чтобы увидеть реализацию.

1
ответ дан 26 November 2019 в 23:43
поделиться

MSDN говорит: "Извлечение значения по ключу происходит очень быстро, почти O(1), потому что класс Dictionary реализован как хэш-таблица." и далее "емкость автоматически увеличивается по мере необходимости путем перераспределения внутреннего массива."

Но вы получите меньше перераспределений, если дадите начальную оценку. Если у вас есть все элементы с самого начала, может пригодиться метод LINQ ToDictionary.

6
ответ дан 26 November 2019 в 23:43
поделиться

У хэш-таблиц обычно есть нечто, называемое коэффициентом загрузки, который увеличивает резервное хранилище ведра при достижении этого порога. IIRC по умолчанию это что-то вроде 0.72. Если у вас идеальное хэширование, это значение может быть увеличено до 1.0.

Также, когда хэш-таблице требуется больше ведер, вся коллекция должна быть перехеширована.

2
ответ дан 26 November 2019 в 23:43
поделиться
Другие вопросы по тегам:

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