Почему словарь предпочтительнее Hashtable в C #?

1109

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

  • 17
    > Это не обязательно правда. Хеш-таблица - это реализация словаря. Типичный для этого, и он может быть по умолчанию в .NET, но по определению он не единственный. Я не уверен, что это требуется стандартом ECMA, но документация MSDN очень четко указывает на то, что он реализован как хеш-таблица. Они даже предоставляют класс SortedList для случаев, когда альтернатива является более разумной.
  • 13
    @ Я всегда думал, что Dictionary - это реализация Hashtable .
Показать ещё 6 комментариев
Теги:
data-structures

19 ответов

1246
Лучший ответ

Для того, что стоит, словарь есть хеш-таблица.

Если вы имели в виду "почему мы используем класс Dictionary вместо класса Hashtable?", тогда это простой ответ: словарь - это общий тип, Hashtable - нет. Это означает, что вы получаете безопасность типов с помощью словаря, потому что вы не можете вставить в него какой-либо случайный объект, и вам не нужно выставлять значения, которые вы вынимаете.

  • 374
    Кроме того, общие коллекции намного быстрее, так как нет никакой упаковки / распаковки
  • 6
    Не уверен насчет Hashtable с приведенным выше оператором, но для ArrayList vs List <t> это правда
Показать ещё 13 комментариев
507

Dictionary < < → → Hashtable:

  • Общий < lt; → > Non-Generic
  • Требуется собственную синхронизацию потоков < < < < < < < < < < < < < < < <
  • Перечисленный элемент: KeyValuePair < < < < → > Перечислимый элемент: DictionaryEntry
  • Более новый ( > .NET 2.0) < < lt; → > Старее (с .NET 1.0)
  • находится в System.Collections.Generic < < < < < < < < < < < < < < < < <
  • Запрос несуществующего ключа вызывает исключение < < < < < < < < < → > Запрос на несуществующий ключ возвращает null
  • возможно немного быстрее для типов значений < lt; → бит медленнее (требуется бокс/распаковка) для типов значений

Dictionary/ Hashtable сходства:

  • Оба являются внутренне hashtables == быстрый доступ к данным с множеством элементов в соответствии с ключом
  • Оба требуют неизменяемые и уникальные ключи
  • Ключи обоих нуждаются в собственном GetHashCode() методе

Похожие коллекции .NET(кандидаты для использования вместо словаря и Hashtable):

  • ConcurrentDictionary - потокобезопасный (можно безопасно получить доступ к нескольким потокам одновременно)
  • HybridDictionary - оптимизированная производительность (для нескольких элементов, а также для многих элементов)
  • OrderedDictionary - значения могут быть доступны через индекс int (по порядку, в котором были добавлены элементы)
  • SortedDictionary - элементы автоматически отсортированы
  • StringDictionary - строго типизировано и оптимизировано для строк
  • 7
    Мы можем использовать concurrentDictionary (в .NET 4.0) для обеспечения безопасности потоков.
  • 11
    @ Guillaume86, именно поэтому вы используете TryGetValue вместо msdn.microsoft.com/en-us/library/bb347013.aspx
Показать ещё 4 комментария
163

Потому что Dictionary - это общий класс (Dictionary<TKey, TValue>), так что доступ к его контенту безопасен по типу (т.е. вам не нужно отбрасывать из Object, как это происходит с Hashtable).

Сравнение

var customers = new Dictionary<string, Customer>();
...
Customer customer = customers["Ali G"];

к

var customers = new Hashtable();
...
Customer customer = customers["Ali G"] as Customer;

Однако Dictionary реализуется как Hashtable внутри, поэтому технически он работает одинаково.

81

FYI: в .NET, Hashtable является потокобезопасным для использования несколькими потоками чтения и одним потоком записи, а в Dictionary общедоступные статические члены являются потокобезопасными, но любые члены экземпляра не гарантируются потокобезопасностью.

Из-за этого нам пришлось изменить все наши словари на Hashtable.

  • 10
    Веселье. Исходный код Dictionary <T> выглядит намного чище и быстрее. Возможно, лучше использовать словарь и реализовать собственную синхронизацию. Если чтение словаря должно быть актуальным, вам просто нужно синхронизировать доступ к методам чтения / записи словаря. Было бы много блокировок, но это было бы правильно.
  • 9
    В качестве альтернативы, если ваши чтения не должны быть абсолютно актуальными, вы можете рассматривать словарь как неизменяемый. Затем вы можете получить ссылку на Словарь и повысить производительность, вообще не синхронизируя чтение (поскольку оно является неизменным и по своей сути поточно-ориентированным). Чтобы обновить его, вы создаете полную обновленную копию словаря в фоновом режиме, а затем просто меняете ссылку на Interlocked.CompareExchange (при условии, что один поток записи; несколько потоков записи потребуют синхронизации обновлений).
Показать ещё 3 комментария
59

В .NET разница между Dictionary<,> и HashTable заключается прежде всего в том, что первый - это общий тип, поэтому вы получаете все преимущества дженериков в терминах проверки статического типа (и уменьшенного бокса, но это не " как люди, как правило, думают с точки зрения производительности - есть определенная стоимость памяти для бокса, хотя).

26

Люди говорят, что Словарь такой же, как хеш-таблица.

Это не обязательно так. Хэш-таблица - это реализация словаря. Типичным в этом случае, и он может быть по умолчанию в .NET, но он по определению не единственный.

Вы могли бы также хорошо реализовать словарь со связанным списком или деревом поиска, это было бы не так эффективно (для некоторых показателей эффективности).

  • 4
    Документы MS гласят: «Получение значения с помощью его ключа выполняется очень быстро, близко к O (1), потому что класс Dictionary <(Of <(TKey, TValue>)>) реализован в виде хеш-таблицы». - так что вам следует гарантировать хеш-таблицу при работе со Dictionary<K,V> . IDictionary<K,V> может быть чем угодно :)
  • 11
    @ rix0rrr - я думаю, что вы поняли, что словарь использует HashTable, а HashTable не использует словарь.
Показать ещё 8 комментариев
18

Collections и Generics полезны для обработки группы объектов. В .NET все объекты коллекций попадают под интерфейс IEnumerable, который, в свою очередь, имеет ArrayList(Index-Value)) и HashTable(Key-Value). После .NET framework 2.0 ArrayList и HashTable были заменены на List и Dictionary. Теперь ArrayList и HashTable больше не используются в современных проектах.

В отличие от HashTable и Dictionary, Dictionary является общим, где Hastable не является общим. Мы можем добавить любой тип объекта к HashTable, но при извлечении нам нужно отбросить его до требуемого типа. Таким образом, это не безопасный тип. Но до Dictionary, объявляя себя, мы можем указать тип ключа и значение, поэтому нет необходимости бросать при извлечении.

Посмотрим на пример:

HashTable

class HashTableProgram
{
    static void Main(string[] args)
    {
        Hashtable ht = new Hashtable();
        ht.Add(1, "One");
        ht.Add(2, "Two");
        ht.Add(3, "Three");
        foreach (DictionaryEntry de in ht)
        {
            int Key = (int)de.Key; //Casting
            string value = de.Value.ToString(); //Casting
            Console.WriteLine(Key + " " + value);
        }

    }
}

Словарь

class DictionaryProgram
{
    static void Main(string[] args)
    {
        Dictionary<int, string> dt = new Dictionary<int, string>();
        dt.Add(1, "One");
        dt.Add(2, "Two");
        dt.Add(3, "Three");
        foreach (KeyValuePair<int, String> kv in dt)
        {
            Console.WriteLine(kv.Key + " " + kv.Value);
        }
    }
}
  • 2
    вместо явного назначения типа данных для KeyValuePair, мы могли бы использовать var. Таким образом, это уменьшит набор текста - foreach (var kv in dt) ... просто предложение.
12

Словарь:

  • Он возвращает/бросает исключение, если мы пытаемся найти ключ, который не существует.

  • Это быстрее, чем Hashtable, потому что нет бокса и unboxing.

  • Только публичные статические члены являются потокобезопасными.

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

    Пример: Dictionary<string, string> <NameOfDictionaryVar> = new Dictionary<string, string>();

  • Dictionay - это безопасная по типу реализация Hashtable, Keys и Values строго типизированы.

Hashtable:

  • Он возвращает null, если мы пытаемся найти ключ, который не существует.

  • Он медленнее, чем словарь, потому что он требует бокса и распаковки.

  • Все члены в Hashtable являются потокобезопасными,

  • Hashtable не является общим типом,

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

  • 0
    «Он возвращает / выдает исключение, если мы пытаемся найти ключ, который не существует». Нет, если вы используете Dictionary.TryGetValue
10

В статье Обширная экспертиза структуры данных с использованием С# в MSDN указано, что существует также разница в стратегии стратегии устранения конфликтов:

Класс Hashtable использует технику, называемую rehashing.

Rehashing работает следующим образом: существует множество хеш-функций, H 1... H n, а при вставке или извлечении элемента из хэша table, изначально используется хэш-функция H 1. Если это приведет к столкновение, H 2, а затем до H n, если это необходимо.

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

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

10

Так как .NET Framework 3.5 также существует HashSet<T>, который предоставляет все преимущества Dictionary<TKey, TValue>, если вам нужны только ключи и нет значений.

Итак, если вы используете Dictionary<MyType, object> и всегда устанавливаете значение null для имитации безопасной хеш-таблицы типа, вы можете подумать о переходе на HashSet<T>.

8

Hashtable - это структура данных с типизированной типизацией, поэтому вы можете добавить ключи и значения любого типа в Hashtable. Класс Dictionary представляет собой безопасную для типа Hashtable реализацию, а ключи и значения строго типизированы. При создании экземпляра Dictionary необходимо указать типы данных как для ключа, так и для значения.

7

Обратите внимание, что MSDN говорит: "Словарь < (Of < (TKey, TValue > ) > ) класс реализуется как хэш-таблица ", а не "Словарь < (Of < (TKey, TValue > ) > ) реализуется как HashTable"

Словарь НЕ реализуется как HashTable, но реализуется в соответствии с концепцией хэш-таблицы. Реализация не связана с классом HashTable из-за использования Generics, хотя внутри Microsoft могла использовать тот же код и заменять символы типа Object на TKey и TValue.

В .NET 1.0 Generics не существует; здесь начались HashTable и ArrayList.

  • 0
    Можете ли вы исправить эту цитату MSDN? Что-то отсутствует или не так; это не грамматически и несколько непонятно.
6

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

Класс словаря имеет ту же функциональность, что и класс Hashtable. Словарь определенного типа (кроме объекта) имеет более высокую производительность, чем Hashtable для типов значений, поскольку элементы Hashtable имеют тип Object, и поэтому бокс и распаковка обычно возникают, если хранить или извлекать тип значения.

Для дальнейшего чтения: Hashtable и типы коллекции словарей

4

Другим важным отличием является то, что Hashtable является потокобезопасным. Hashtable имеет встроенную систему защиты многопотокового считывателя/одиночной записи (MR/SW), что означает, что Hashtable позволяет одиночной записи вместе с несколькими считывателями без блокировки.

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

Далее уточнить:

Hashtable обеспечивает некоторую безопасность потоков через свойство Synchronized, которое возвращает потокобезопасную оболочку вокруг коллекции. Обертка работает, блокируя всю коллекцию при каждой операции добавления или удаления. Поэтому каждый поток, пытающийся получить доступ к коллекции, должен дождаться, пока его очередь займет один замок. Это не является масштабируемым и может привести к значительной деградации производительности для больших коллекций. Кроме того, дизайн не полностью защищен от условий гонки.

Классы коллекции .NET Framework 2.0, такие как List<T>, Dictionary<TKey, TValue> и т.д., не обеспечивают ни одной синхронизации потоков; код пользователя должен обеспечивать всю синхронизацию, когда элементы добавляются или удаляются одновременно на нескольких потоках.

Если вам нужна безопасность типов, а также безопасность потоков, используйте параллельные классы коллекций в .NET Framework. Дальнейшее чтение здесь.

Дополнительная разница заключается в том, что когда мы добавляем несколько записей в словаре, порядок, в который добавляются записи, поддерживается. Когда мы извлекаем предметы из Словаря, мы получим записи в том же порядке, в который мы их вставили. В то время как Hashtable не сохраняет порядок вставки.

  • 1
    «Некоторый поток безопасности» не то же самое, что «Поток безопасности»
4

Dictionary<> является универсальным типом, поэтому он безопасен.

Вы можете вставить любой тип значения в HashTable, и иногда это может вызвать исключение. Но Dictionary<int> будет принимать только целочисленные значения и аналогично Dictionary<string> будет принимать только строки.

Итак, лучше использовать Dictionary<> вместо HashTable.

4

Еще одно отличие, которое я могу выяснить:

Мы не можем использовать Dictionary < KT, VT > (generics) с веб-службами. Причина в том, что стандарт веб-службы не поддерживает стандарт дженериков.

  • 0
    Мы можем использовать общие списки (List <string>) в веб-сервисе на основе мыла. Но мы не можем использовать словарь (или хеш-таблицу) в веб-сервисе. Я думаю, что причина этого в том, что .net xmlserializer не может обработать объект словаря.
3

Во время выполнения операций Словарь выполняется быстрее, потому что нет бокса/распаковки (значениям нет необходимости в боксе), в то время как в боксе/распаковке Hashtable (важно, что нужно делать бокс), и которое может иметь потребление памяти, а также штрафы за производительность.

Когда мы добавляем несколько записей в словарь, порядок, в котором добавляются записи, сохраняется. Когда мы извлекаем предметы из Словаря, мы получим записи в том же порядке, в который мы их вставили. В то время как мы добавляем те же записи в Hashtable, заказ не поддерживается. Подробнее о.... Словарь и хеш-таблица

Rj

3

HashTable:

Ключ/значение будет преобразовываться в тип объекта (бокс) при сохранении в кучу.

Ключ/значение необходимо преобразовать в желаемый тип во время чтения из кучи.

Эти операции очень дорогостоящие. Нам нужно как можно больше избежать бокса/распаковки.

Словарь: Общий вариант HashTable.

Нет бокса/распаковки. Конверсий не требуется.

-3

В соответствии с тем, что я вижу, используя .NET Reflector:

[Serializable, ComVisible(true)]
public abstract class DictionaryBase : IDictionary, ICollection, IEnumerable
{
    // Fields
    private Hashtable hashtable;

    // Methods
    protected DictionaryBase();
    public void Clear();
.
.
.
}
Take note of these lines
// Fields
private Hashtable hashtable;

Таким образом, мы можем быть уверены, что DictionaryBase внутренне использует HashTable.

  • 15
    System.Collections.Generic.Dictionary <TKey, TValue> не является производным от DictionaryBase.
  • 0
    «Таким образом, мы можем быть уверены, что DictionaryBase использует HashTable внутри». - Это хорошо, но это не имеет никакого отношения к вопросу.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню