Dictionary vs Hashtable в C#: почему словарь всегда побеждает?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Dictionary vs Hashtable: битва коллекций в C#

Представьте себе ситуацию: вы пишете код на C#, и вам нужно быстро сохранять и извлекать данные по ключу. Вы открываете документацию и видите два похожих класса — Dictionary и Hashtable. Оба работают по принципу "ключ-значение", оба обещают молниеносный доступ к данным. Но какой выбрать?

Если вы задавались этим вопросом, вы не одиноки. На Stack Overflow тема "Why is Dictionary preferred over Hashtable in C#?" собрала тысячи просмотров и сотни голосов. И сегодня мы разберем этот вопрос раз и навсегда. Спойлер: Dictionary почти всегда выигрывает. Но давайте разберемся почему.

Краткий экскурс в историю: откуда взялся Hashtable?

Чтобы понять суть противостояния, нужно заглянуть в историю C#. Когда платформа .NET только появилась (версия 1.0), разработчикам был доступен класс Hashtable из пространства имен System.Collections. Это была классическая хеш-таблица, которая могла хранить любые объекты. Звучит круто, правда? Но была одна проблема — отсутствие типизации.

С выходом .NET 2.0 и появлением обобщений (generics) Microsoft представила новую коллекцию — Dictionary<TKey, TValue> из пространства имен System.Collections.Generic. И вот тут-то началась настоящая магия.

Типобезопасность: главное преимущество Dictionary

Давайте посмотрим на конкретный пример кода. Вот как выглядит работа с Hashtable:

// Старый способ: Hashtable
Hashtable hashtable = new Hashtable();
hashtable.Add("ключ1", 123);
hashtable.Add("ключ2", "строка"); // Ошибка? Нет, скомпилируется!
hashtable.Add(456, "еще строка"); // Ключ тоже может быть любым объектом

// А теперь попробуем достать данные
int value = (int)hashtable["ключ2"]; // Ой! InvalidCastException в рантайме!

Видите проблему? Hashtable принимает любые типы — и для ключей, и для значений. Компилятор пропустит любую ошибку, и вы узнаете о ней только во время выполнения программы. Это прямой путь к багам, которые сложно отловить.

А теперь посмотрим на Dictionary:

// Новый, правильный способ: Dictionary
Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add("ключ1", 123);
// dictionary.Add("ключ2", "строка"); // Ошибка компиляции! Тип не совпадает
// dictionary.Add(456, "еще строка"); // Тоже ошибка — ключ должен быть string

// Извлекаем данные без приведений
int value = dictionary["ключ1"]; // Чисто, безопасно, красиво

Dictionary — типобезопасен. Вы явно указываете, какие типы будут у ключей и значений. Компилятор проверяет это на этапе сборки. Никаких неожиданных исключений во время выполнения — код либо работает, либо не компилируется.

Производительность: кто быстрее?

Второй важный аспект — производительность. И тут Dictionary снова вырывается вперед. Почему? Всё дело в упаковке (boxing) и распаковке (unboxing).

Когда вы работаете с Hashtable, все значимые типы (int, double, struct и т.д.) упаковываются в объекты. Это создает нагрузку на сборщик мусора и замедляет работу. Давайте проверим на практике:

// Hashtable: упаковка/распаковка
Hashtable ht = new Hashtable();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
{
    ht.Add(i, i); // Каждый int упаковывается в object
}
sw.Stop();
Console.WriteLine($"Hashtable добавление: {sw.ElapsedMilliseconds} мс");

// Dictionary: без упаковки
Dictionary<int, int> dict = new Dictionary<int, int>();
sw.Restart();
for (int i = 0; i < 1000000; i++)
{
    dict.Add(i, i); // Никакой упаковки — работаем с int напрямую
}
sw.Stop();
Console.WriteLine($"Dictionary добавление: {sw.ElapsedMilliseconds} мс");

В реальных тестах Dictionary обычно показывает прирост производительности в 2-3 раза по сравнению с Hashtable. А при работе с большими объемами данных разница становится еще заметнее.

Читаемость и поддержка кода

Третий важный фактор — читаемость кода. Посмотрите на эти два фрагмента:

// Hashtable: что за тип? Непонятно
Hashtable users = new Hashtable();
users.Add("john", new User("John", 25));
User user = (User)users["john"];

// Dictionary: всё ясно с первого взгляда
Dictionary<string, User> users = new Dictionary<string, User>();
users.Add("john", new User("John", 25));
User user = users["john"];

Во втором случае вы сразу видите структуру данных: ключи — строки, значения — объекты User. Не нужно гадать, что лежит внутри. Это особенно важно в больших проектах, где код читают десятки разработчиков.

Дополнительные фишки Dictionary

Dictionary предлагает несколько удобных методов, которых нет в Hashtable:

  • TryGetValue — безопасное извлечение значения без обработки исключений
  • ContainsKey — быстрая проверка наличия ключа
  • Remove — удаление элемента
  • LINQ-совместимость — можно использовать все прелести LINQ запросов
Dictionary<string, int> scores = new Dictionary<string, int>();
scores["Alice"] = 95;
scores["Bob"] = 87;

// Безопасное извлечение
if (scores.TryGetValue("Charlie", out int charlieScore))
{
    Console.WriteLine($"Charlie scored: {charlieScore}");
}
else
{
    Console.WriteLine("Charlie not found");
}

// LINQ запросы — легко и элегантно
var topStudents = scores.Where(s => s.Value > 90)
                        .Select(s => s.Key)
                        .ToList();

Когда Hashtable всё ещё может быть полезен?

Честно говоря, в современном C# (версии 8.0 и выше) нет реальных причин использовать Hashtable. Даже если вам нужно работать с разнородными данными, лучше использовать Dictionary<object, object> — это даст больше гибкости и возможностей для рефакторинга в будущем.

Единственный сценарий, когда Hashtable может быть оправдан — это работа с очень старым кодом, написанным до .NET 2.0, или взаимодействие с COM-объектами, которые требуют именно необобщенную коллекцию. Но даже в этих случаях стоит подумать о миграции на Dictionary.

Практические рекомендации

Чтобы закрепить материал, вот несколько практических советов:

  1. Всегда использу

Блоги

Рекомендаций по книгам