Perché il dizionario è preferito su Hashtable in C #?


1396

Nella maggior parte dei linguaggi di programmazione, i dizionari sono preferiti rispetto agli hashtabili. Quali sono le ragioni alla base di ciò?


21
> Questo non è necessariamente vero. Una tabella hash è un'implementazione di un dizionario. Un tipico a quello, e potrebbe essere quello predefinito in .NET, ma non è per definizione l'unico. Non sono sicuro che ciò sia richiesto dallo standard ECMA, ma la documentazione MSDN lo definisce chiaramente come implementato come hashtable. Forniscono persino la classe SortedList per i periodi in cui un'alternativa è più ragionevole.
Promessa il

15
@Promit Ho sempre pensato che Dictionaryfosse un'implementazione di Hashtable.
b1nary.atr0phy

2
Penso che il motivo sia che in un dizionario è possibile definire il tipo di chiave e il valore per la propria scelta. Hashtable può solo prendere oggetti e salva le coppie in base all'hash (da object.GetHashCode ()).
Radinatore,

2
@Dan La tua affermazione è piuttosto errata ... una tabella hash contiene solo un'istanza di ogni chiave e una ricerca non produce mai più voci; se si desidera associare più valori a ciascuna chiave, impostare il valore della tabella hash in un elenco di valori. Non esiste una struttura di dati come "un dizionario" ... Dizionario è semplicemente il nome che alcune librerie usano per la loro tabella hash. ad esempio, viene chiamata la tabella hash non generica di C # HashTable. Quando hanno aggiunto generici alla lingua, hanno chiamato la versione generica Dictionary. Entrambi sono tabelle hash.
Jim Balter,

3
@Dan La tua affermazione è errata ... una tabella hash ( en.wikipedia.org/wiki/Hash_table ) è un'implementazione particolare di un dizionario, noto anche come array associativo ( en.wikipedia.org/wiki/Associative_array ), ed essendo un dizionario, contiene solo un'istanza di ogni chiave e una ricerca non produce mai più voci; se si desidera associare più valori a ciascuna chiave, impostare il valore della tabella hash in un elenco di valori. E le classi .NET Dictionary e Hashtable sono entrambe tabelle hash.
Jim Balter,

Risposte:


1568

Per quello che vale, un dizionario è (concettualmente) una tabella hash.

Se intendevi "perché usiamo la Dictionary<TKey, TValue>classe anziché la Hashtableclasse?", Allora è una risposta semplice: Dictionary<TKey, TValue>è un tipo generico, Hashtablenon lo è. Ciò significa che si ottiene la sicurezza del tipo con Dictionary<TKey, TValue>, perché non è possibile inserire alcun oggetto casuale in esso e non è necessario eseguire il cast dei valori estratti.

È interessante notare che l' Dictionary<TKey, TValue>implementazione in .NET Framework si basa sul Hashtable, come puoi vedere da questo commento nel suo codice sorgente:

Il dizionario generico è stato copiato dalla fonte di Hashtable

fonte


393
E anche le raccolte generiche sono molto più veloci in quanto non c'è boxe / unboxing
Chris S,

6
Non sono sicuro di un Hashtable con la dichiarazione precedente, ma per ArrayList vs List <t> è vero
Chris S,

36
Hashtable usa Object per contenere le cose internamente (solo un modo non generico per farlo), quindi dovrebbe anche box / unbox.
Guvante,

16
@BrianJ: una "tabella hash" (due parole) è il termine informatico per questo tipo di struttura; Il dizionario è un'implementazione specifica. Una tabella hash corrisponde approssimativamente a un dizionario <oggetto, oggetto> (sebbene con interfacce leggermente diverse), ma entrambi sono implementazioni del concetto di tabella hash. E ovviamente, solo per confondere ulteriormente le cose, alcune lingue chiamano le loro tabelle hash "dizionari" (ad esempio Python) - ma il termine CS corretto è ancora tabella hash.
Michael Madsen,

32
@BrianJ: Sia HashTable(classe) che Dictionary(classe) sono tabelle hash (concetto), ma a HashTablenon è a Dictionary, né è Dictionarya HashTable. Sono usati in modo molto simile e Dictionary<Object,Object>possono agire nello stesso modo non tipizzato di a HashTable, ma non condividono direttamente alcun codice (anche se è probabile che le parti vengano implementate in modo molto simile).
Michael Madsen,

625

Dictionary<<< >>> Hashtabledifferenze:

  • Generico <<< >>> Non generico
  • Richiede la sincronizzazione del thread <<< >>> Offre una versione thread-safe tramite il Synchronized()metodo
  • Articolo elencato: KeyValuePair<<< >>> Articolo elencato:DictionaryEntry
  • Più recenti (> .NET 2.0 ) <<< >>> Più vecchi (da .NET 1.0 )
  • è in System.Collections.Generic <<< >>> è in System.Collections
  • La richiesta alla chiave inesistente genera un'eccezione <<< >>> La richiesta alla chiave inesistente restituisce null
  • potenzialmente un po ' più veloce per i tipi di valore <<< >>> un po' più lento (richiede boxe / unboxing) per i tipi di valore

Dictionary/ Hashtablesomiglianze:

  • Entrambi sono hashtables interni == accesso rapido ai dati di molti elementi in base alla chiave
  • Entrambi hanno bisogno di chiavi immutabili e uniche
  • Le chiavi di entrambi hanno bisogno del proprio GetHashCode()metodo

Collezioni .NET simili (candidati da utilizzare al posto di Dizionario e Hashtable):

  • ConcurrentDictionary- thread safe (è possibile accedere in modo sicuro da più thread contemporaneamente)
  • HybridDictionary- prestazioni ottimizzate (per pochi articoli e anche per molti articoli)
  • OrderedDictionary- è possibile accedere ai valori tramite l'indice int (in base all'ordine in cui sono stati aggiunti gli articoli)
  • SortedDictionary- articoli ordinati automaticamente
  • StringDictionary- fortemente tipizzato e ottimizzato per le stringhe

11
@ Guillaume86, questo è il motivo per cui si utilizza TryGetValue invece msdn.microsoft.com/en-us/library/bb347013.aspx
Trident D'Gao

2
+1 per StringDictionary... btw StringDictionarynon è lo stesso di Dictionary<string, string>quando si utilizza il costruttore predefinito.
Cheng Chen,

ParallelExtensionsExtras @ code.msdn.microsoft.com/windowsdesktop/… contiene un oggetto ObservableConcurrentDictionary che è un ottimo legame tra abete e concorrenza.
VoteCoffee

3
spiegazione fantastica, è davvero bello che tu abbia elencato anche le somiglianze per ridurre le domande che potrebbero venire in mente
MK


178

Perché Dictionaryè una classe generica ( Dictionary<TKey, TValue>), in modo che l'accesso al suo contenuto sia sicuro per il tipo (cioè non è necessario eseguire il cast da Object, come si fa con a Hashtable).

Confrontare

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

per

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

Tuttavia, Dictionaryè implementato internamente come tabella hash, quindi tecnicamente funziona allo stesso modo.


88

Cordiali saluti: In .NET, il Hashtablethread è sicuro per l'uso da parte di più thread di lettura e un singolo thread di scrittura, mentre in Dictionarypubblico i membri statici sono thread-safe, ma i membri di ogni istanza non sono garantiti come thread-safe.

Abbiamo dovuto cambiare tutti i nostri dizionari in Hashtablequesto modo.


10
Divertimento. Il codice sorgente Dizionario <T> sembra molto più pulito e veloce. Potrebbe essere meglio usare Dizionario e implementare la propria sincronizzazione. Se le letture del dizionario devono assolutamente essere aggiornate, dovrai semplicemente sincronizzare l'accesso ai metodi di lettura / scrittura del dizionario. Sarebbe un sacco di blocco, ma sarebbe corretto.
Triynko,

10
In alternativa, se le tue letture non devono essere assolutamente aggiornate, potresti considerare il dizionario immutabile. È quindi possibile acquisire un riferimento al dizionario e ottenere prestazioni non sincronizzando affatto le letture (poiché è immutabile e intrinsecamente sicuro per i thread). Per aggiornarlo, costruisci una copia aggiornata completa del Dizionario in background, quindi scambia il riferimento con Interlocked.CompareExchange (presupponendo un singolo thread di scrittura; più thread di scrittura richiederebbero la sincronizzazione degli aggiornamenti).
Triynko,

38
.Net 4.0 ha aggiunto la ConcurrentDictionaryclasse che ha implementato tutti i metodi pubblici / protetti come thread-safe. Se non hai bisogno di supportare piattaforme legacy, questo ti permetterebbe di sostituire il Hashtablecodice multithread: msdn.microsoft.com/en-us/library/dd287191.aspx
Dan sta scherzando alla luce del fuoco

anonimo in soccorso. Bella risposta.
unkulunkulu l'

5
Ricordo di aver letto che HashTable è solo thread-reader sicuro per i lettori nello scenario in cui le informazioni non vengono mai cancellate dalla tabella. Se un lettore chiede un oggetto che si trova nella tabella mentre un oggetto diverso viene eliminato e il lettore dovrebbe cercare l'elemento in più di un posto, è possibile che mentre il lettore cerca lo scrittore possa spostare l'oggetto da un posto che non è stato esaminato a uno che ha, risultando quindi in un falso rapporto che l'articolo non esiste.
supercat

68

In .NET, la differenza tra Dictionary<,>e HashTableè principalmente che il primo è un tipo generico, quindi ottieni tutti i vantaggi dei generici in termini di controllo statico del tipo (e boxe ridotto, ma questo non è così grande come la gente tende a pensare termini di prestazione - c'è un costo di memoria definito per la boxe).


34

La gente dice che un dizionario è lo stesso di una tabella hash.

Questo non è necessariamente vero. Una tabella hash è un modo per implementare un dizionario. Un tipico a quello, e potrebbe essere quello predefinito in .NET nella Dictionaryclasse, ma non è per definizione l'unico.

Potresti ugualmente implementare un dizionario usando un elenco collegato o un albero di ricerca, non sarebbe altrettanto efficiente (per una metrica di efficiente).


4
I documenti MS dicono: "Il recupero di un valore usando la sua chiave è molto veloce, vicino a O (1), perché la classe Dictionary <(Of <(TKey, TValue>)>) è implementata come una tabella hash." - quindi dovresti avere una hashtable quando ti occupi di Dictionary<K,V>. IDictionary<K,V>potrebbe essere qualsiasi cosa, però :)
snemarch

13
@ rix0rrr - Penso che tu l'abbia indietro, un Dizionario usa una HashTable e non una HashTable usa un Dizionario.
Joseph Hamilton,

8
@JosephHamilton - rix0rrr ha capito bene: "Una tabella hash è un'implementazione di un dizionario ". Intende il concetto di "dizionario", non la classe (notare le lettere minuscole). Concettualmente, una tabella hash implementa un'interfaccia di dizionario. In .NET, Dizionario utilizza una tabella hash per implementare IDictionary. È disordinato;)
Robert Hensing l'

Stavo parlando in .NET, poiché è quello a cui ha fatto riferimento nella sua risposta.
Joseph Hamilton,

2
@JosephHamilton: strumenti (o implementazione di ) non significano nemmeno in remoto la stessa cosa degli usi . Piuttosto il contrario. Forse sarebbe stato più chiaro se lo avesse detto in modo leggermente diverso (ma con lo stesso significato): "una tabella hash è un modo per implementare un dizionario". Cioè, se si desidera la funzionalità di un dizionario, un modo per farlo (per implementare il dizionario) è utilizzare una tabella hash.
ToolmakerSteve

21

Collectionse Genericssono utili per la gestione di un gruppo di oggetti. In .NET, tutti gli oggetti delle raccolte rientrano nell'interfaccia IEnumerable, che a sua volta ha ArrayList(Index-Value))& HashTable(Key-Value). Dopo .NET Framework 2.0, ArrayListe HashTablesono stati sostituiti con List& Dictionary. Ora, i Arraylist& HashTablenon sono più utilizzati nei progetti di oggi.

Venire alla differenza tra HashTable& Dictionary, Dictionaryè generico dove Hastablenon è generico. Possiamo aggiungere qualsiasi tipo di oggetto HashTable, ma durante il recupero dobbiamo lanciarlo sul tipo richiesto. Quindi, non è sicuro. Ma per dictionarydichiarare se stesso possiamo specificare il tipo di chiave e valore, quindi non è necessario eseguire il cast durante il recupero.

Diamo un'occhiata a un esempio:

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);
        }

    }
}

Dizionario,

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
invece di assegnare esplicitamente il tipo di dati per KeyValuePair, potremmo usare var. Quindi, questo ridurrebbe la digitazione - foreach (var kv in dt) ... solo un suggerimento.
Ron,

16

Dizionario:

  • Restituisce / genera Eccezione se proviamo a trovare una chiave che non esiste.

  • È più veloce di un Hashtable perché non c'è boxe e unboxing.

  • Solo i membri statici pubblici sono thread-safe.

  • Il dizionario è un tipo generico, il che significa che possiamo usarlo con qualsiasi tipo di dati (durante la creazione, è necessario specificare i tipi di dati per chiavi e valori).

    Esempio: Dictionary<string, string> <NameOfDictionaryVar> = new Dictionary<string, string>();

  • Dictionay è un'implementazione di Hashtable sicura per i tipi Keyse Valuesfortemente tipizzata.

hashtable:

  • Restituisce null se proviamo a trovare una chiave che non esiste.

  • È più lento del dizionario perché richiede boxe e unboxing.

  • Tutti i membri di un Hashtable sono thread-safe,

  • Hashtable non è un tipo generico,

  • Hashtable è una struttura di dati tipicamente libera, possiamo aggiungere chiavi e valori di qualsiasi tipo.


"Restituisce / genera un'eccezione se proviamo a trovare una chiave che non esiste." Non se usiDictionary.TryGetValue
Jim Balter il

16

L' esame approfondito delle strutture di dati che utilizzano l' articolo C # su MSDN afferma che esiste anche una differenza nella strategia di risoluzione delle collisioni :

La classe Hashtable utilizza una tecnica denominata rifacimento .

Il rehashing funziona come segue: esiste un set di funzioni hash differenti, H 1 ... H n , e quando si inserisce o recupera un elemento dalla tabella hash, inizialmente viene utilizzata la funzione hash H 1 . Se questo porta a una collisione, viene invece provato H 2 e, se necessario , fino a H n .

Il dizionario utilizza una tecnica nota come concatenamento .

Con il rifacimento, in caso di collisione viene ricalcolato l'hash e viene provato il nuovo slot corrispondente a un hash. Con il concatenamento, tuttavia, viene utilizzata una struttura di dati secondaria per contenere eventuali collisioni . In particolare, ogni slot nel Dizionario ha una matrice di elementi che si associano a quel bucket. In caso di collisione, l'elemento di collisione viene anteposto all'elenco dei bucket.


16

Da .NET Framework 3.5 esiste anche un servizio HashSet<T>che fornisce tutti i vantaggi di Dictionary<TKey, TValue>se hai bisogno solo delle chiavi e senza valori.

Quindi, se usi a Dictionary<MyType, object>e imposti sempre il valore per nullsimulare il tipo hash table sicuro dovresti forse considerare di passare a HashSet<T>.


14

Si Hashtabletratta di una struttura di dati tipicamente libera, quindi è possibile aggiungere chiavi e valori di qualsiasi tipo a Hashtable. La Dictionaryclasse è Hashtableun'implementazione sicura e le chiavi e i valori sono fortemente tipizzati. Quando si crea Dictionaryun'istanza, è necessario specificare i tipi di dati sia per la chiave che per il valore.


11

Si noti che MSDN dice: "La classe Dictionary <(Of <(TKey, TValue>)>) è implementata come una tabella hash ", non "La classe Dictionary <(Of <(TKey, TValue>)>) è implementata come una HashTable "

Il dizionario NON è implementato come HashTable, ma è implementato seguendo il concetto di una tabella hash. L'implementazione non è correlata alla classe HashTable a causa dell'uso di Generics, sebbene internamente Microsoft avrebbe potuto usare lo stesso codice e sostituito i simboli di tipo Object con TKey e TValue.

In .NET 1.0 Generics non esisteva; questo è dove originariamente iniziarono HashTable e ArrayList.


Riesci a correggere quella citazione MSDN? Qualcosa è mancante o sbagliato; non è grammaticale e in qualche modo incomprensibile.
Peter Mortensen,

10

HashTable:

Chiave / valore verranno convertiti in un tipo di oggetto (boxe) durante la memorizzazione nell'heap.

Chiave / valore deve essere convertito nel tipo desiderato durante la lettura dall'heap.

Queste operazioni sono molto costose. Dobbiamo evitare il più possibile il pugilato / unboxing.

Dizionario: variante generica di HashTable.

Nessun boxe / unboxing. Nessuna conversione richiesta.


8

Un oggetto Hashtable è costituito da bucket che contengono gli elementi della raccolta. Un bucket è un sottogruppo virtuale di elementi all'interno di Hashtable, che semplifica e velocizza la ricerca e il recupero rispetto alla maggior parte delle raccolte .

La classe Dictionary ha le stesse funzionalità della classe Hashtable. Un dizionario di un tipo specifico (diverso dall'oggetto) ha prestazioni migliori rispetto a un Hashtable per i tipi di valore perché gli elementi di Hashtable sono di tipo Object e, pertanto, si verificano tipicamente boxing e unboxing se si memorizza o recupera un tipo di valore.

Per ulteriori letture: Hashtable e Tipi di raccolte di dizionari


7

Un'altra differenza importante è che Hashtable è thread-safe. Hashtable ha la sicurezza del thread multi-reader / single writer (MR / SW) incorporata, il che significa che Hashtable consente ONE writer insieme a più lettori senza blocco.

Nel caso di Dictionary non esiste sicurezza thread; se hai bisogno della sicurezza dei thread devi implementare la tua sincronizzazione.

Per elaborare ulteriormente:

Hashtable fornisce un po 'di sicurezza del thread attraverso Synchronized proprietà, che restituisce un wrapper thread-safe attorno alla raccolta. Il wrapper funziona bloccando l'intera raccolta su ogni operazione di aggiunta o rimozione. Pertanto, ogni thread che sta tentando di accedere alla raccolta deve attendere il proprio turno per ottenere un blocco. Questo non è scalabile e può causare un significativo peggioramento delle prestazioni per raccolte di grandi dimensioni. Inoltre, il design non è completamente protetto dalle condizioni di gara.

Le classi di raccolta di .NET Framework 2.0 come List<T>, Dictionary<TKey, TValue>, ecc. Non forniscono alcuna sincronizzazione dei thread; il codice utente deve fornire tutta la sincronizzazione quando gli elementi vengono aggiunti o rimossi contemporaneamente su più thread

Se è necessaria la sicurezza dei tipi e la sicurezza dei thread, utilizzare le classi di raccolte simultanee in .NET Framework. Ulteriori letture qui .

Un'ulteriore differenza è che quando si aggiungono più voci nel dizionario, viene mantenuto l'ordine in cui vengono aggiunte le voci. Quando recuperiamo gli articoli dal Dizionario, otterremo i record nello stesso ordine in cui li abbiamo inseriti. Considerando che Hashtable non mantiene l'ordine di inserimento.


Da quanto ho capito, Hashsetgarantisce la sicurezza del thread MR / SW in scenari di utilizzo che non comportano eliminazioni . Penso che potrebbe essere stato progettato per essere completamente sicuro per MR / SW, ma gestire le cancellazioni in modo sicuro aumenta notevolmente il costo della sicurezza MR / SW. Mentre la progettazione di Dictionaryavrebbe potuto offrire la sicurezza MR / SW a costi minimi in scenari di non cancellazione, penso che MS volesse evitare di considerare gli scenari di non cancellazione come "speciali".
supercat

5

Un'altra differenza che posso capire è:

Non possiamo usare il dizionario <KT, VT> (generici) con i servizi web. Il motivo è che nessuno standard di servizi web supporta lo standard generics.


Possiamo usare elenchi generici (Elenco <stringa>) nel servizio Web basato su sapone. Ma non possiamo usare il dizionario (o la tabella) in un servizio web. Penso che il motivo sia che xnet xmlserializer non è in grado di gestire l'oggetto dizionario.
Siddharth,

5

Dictionary<> è un tipo generico e quindi è sicuro.

È possibile inserire qualsiasi tipo di valore in HashTable e questo può talvolta generare un'eccezione. Ma Dictionary<int>accetta solo valori interi e allo stesso modo Dictionary<string>accetta solo stringhe.

Quindi, è meglio usare Dictionary<>invece di HashTable.


0

Nella maggior parte dei linguaggi di programmazione, i dizionari sono preferiti rispetto agli hashtabili

Non penso sia necessariamente vero, la maggior parte delle lingue ha l'una o l'altra, a seconda della terminologia che preferiscono .

In C #, tuttavia, la chiara ragione (per me) è che C # HashTables e altri membri dello spazio dei nomi System.Collections sono in gran parte obsoleti. Erano presenti in c # V1.1. Sono stati sostituiti da C # 2.0 dalle classi generiche nello spazio dei nomi System.Collections.Generic.


Uno dei vantaggi di una tabella hash rispetto a un dizionario è che se una chiave non esiste in un dizionario, genererà un errore. Se una chiave non esiste in una tabella hash, restituisce semplicemente null.
Bill Norman,

In C # eviterei comunque di usare System.Collections.Hashtable in quanto non hanno il vantaggio dei generici. Puoi usare TryGetValue o HasKey di Dictionary se non sai se la chiave esisterà.
kristianp,

Whoops, non HasKey, dovrebbe essere ContainsKey.
Kristianp,

-3

Secondo quello che vedo usando .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;

Quindi possiamo essere sicuri che DictionaryBase utilizza internamente una HashTable.


16
System.Collections.Generic.Dictionary <TKey, TValue> non deriva da DictionaryBase.
snemarch,

"Quindi possiamo essere sicuri che DictionaryBase utilizza internamente una HashTable." - È carino, ma non ha nulla a che fare con la domanda.
Jim Balter,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.