¿Por qué se prefiere Dictionary sobre Hashtable en C #?

1396

En la mayoría de los lenguajes de programación, los diccionarios son preferibles a las tablas hash. ¿Cuáles son las razones detrás de eso?

Nakul Chaudhary
fuente
21
> Esto no es necesariamente cierto. Una tabla hash es una implementación de un diccionario. Una típica en eso, y puede ser la predeterminada en .NET, pero no es, por definición, la única. No estoy seguro de que esto sea requerido por el estándar ECMA, pero la documentación de MSDN lo señala claramente como implementado como una tabla hash. Incluso proporcionan la clase SortedList para momentos en que una alternativa es más razonable.
Prometido
15
@Promit Siempre pensé que Dictionaryera una implementación de Hashtable.
b1nary.atr0phy
2
Creo que la razón es que en un diccionario puedes definir el tipo de clave y el valor para ti mismo. Hashtable solo puede tomar objetos y guarda los pares en función del hash (from object.GetHashCode ()).
Radinator
2
@Dan Su reclamo está bastante equivocado ... una tabla hash solo contiene una instancia de cada clave, y una búsqueda nunca produce múltiples entradas; si desea asociar varios valores con cada clave, haga que el valor de la tabla hash sea una lista de valores. No existe una estructura de datos como "un diccionario" ... Diccionario es simplemente el nombre que algunas bibliotecas usan para su tabla hash. por ejemplo, se llama la tabla hash no genérica de C # HashTable. Cuando agregaron genéricos al idioma, llamaron la versión genérica Dictionary. Ambas son tablas hash.
Jim Balter
3
@Dan Su reclamo está equivocado ... una tabla hash ( en.wikipedia.org/wiki/Hash_table ) es una implementación particular de un diccionario, también conocido como una matriz asociativa ( en.wikipedia.org/wiki/Associative_array ), y, siendo un diccionario, solo contiene una instancia de cada clave, y una búsqueda nunca produce múltiples entradas; si desea asociar varios valores con cada clave, haga que el valor de la tabla hash sea una lista de valores. Y las clases de .NET Dictionary y Hashtable son tablas hash.
Jim Balter

Respuestas:

1568

Por lo que vale, un Diccionario es (conceptualmente) una tabla hash.

Si quisiste decir "¿por qué usamos la Dictionary<TKey, TValue>clase en lugar de la Hashtableclase?", Entonces es una respuesta fácil: Dictionary<TKey, TValue>es un tipo genérico, Hashtableno lo es. Eso significa que obtienes seguridad de tipografía Dictionary<TKey, TValue>, porque no puedes insertar ningún objeto aleatorio en él y no tienes que emitir los valores que extraes.

Curiosamente, la Dictionary<TKey, TValue>implementación en .NET Framework se basa en Hashtable, como se puede ver en este comentario en su código fuente:

El diccionario genérico se copió de la fuente de Hashtable

Fuente

Michael Madsen
fuente
393
Y también las colecciones genéricas son mucho más rápidas ya que no hay box / unboxing
Chris S
66
No estoy seguro acerca de un Hashtable con la declaración anterior, pero para ArrayList vs List <t> es cierto
Chris S
36
Hashtable usa Object para guardar cosas internamente (solo una forma no genérica de hacerlo), por lo que también tendría que box / unbox.
Guvante
16
@BrianJ: Una "tabla hash" (dos palabras) es el término informático para este tipo de estructura; El diccionario es una implementación específica. Una tabla hash corresponde aproximadamente a un diccionario <objeto, objeto> (aunque con interfaces ligeramente diferentes), pero ambas son implementaciones del concepto de tabla hash. Y, por supuesto, para confundir aún más las cosas, algunos lenguajes llaman a sus tablas hash "diccionarios" (por ejemplo, Python), pero el término CS adecuado sigue siendo hash table.
Michael Madsen
32
@BrianJ: Tanto HashTable(clase) como Dictionary(clase) son tablas hash (concepto), pero a HashTableno es a Dictionary, ni es Dictionarya HashTable. Se usan de manera muy similar y Dictionary<Object,Object>pueden actuar de la misma manera sin tipo que a HashTable, pero no comparten directamente ningún código (aunque es probable que las partes se implementen de manera muy similar).
Michael Madsen
625

Dictionary<<< >>> Hashtablediferencias:

  • Genérico <<< >>> No genérico
  • Necesita sincronización de hilo propia <<< >>> Ofrece una versión segura de hilo a través del Synchronized()método
  • Artículo KeyValuePairenumerado : <<< >>> Artículo enumerado:DictionaryEntry
  • Más reciente (> .NET 2.0 ) <<< >>> Anterior (desde .NET 1.0 )
  • está en System.Collections.Generic <<< >>> está en System.Collections
  • Solicitud a clave no existente arroja excepción <<< >>> Solicitud a clave no existente devuelve nulo
  • potencialmente un poco más rápido para los tipos de valor <<< >>> un poco más lento (necesita boxing / unboxing) para los tipos de valor

Dictionary/ Hashtablesimilitudes:

  • Ambos son tablas hash internamente == acceso rápido a datos de muchos elementos según la clave
  • Ambos necesitan llaves inmutables y únicas.
  • Las claves de ambos necesitan un GetHashCode()método propio

Colecciones .NET similares (candidatos para usar en lugar de Diccionario y Hashtable):

  • ConcurrentDictionary- hilo seguro (se puede acceder de forma segura desde varios hilos al mismo tiempo)
  • HybridDictionary- rendimiento optimizado (para pocos artículos y también para muchos artículos)
  • OrderedDictionary- Se puede acceder a los valores a través del índice int (por orden en que se agregaron los elementos)
  • SortedDictionary- artículos ordenados automáticamente
  • StringDictionary- fuertemente tipado y optimizado para cadenas
Marcel Toth
fuente
11
@ Guillaume86, esta es la razón por la que usas TryGetValue en su lugar msdn.microsoft.com/en-us/library/bb347013.aspx
Trident D'Gao
2
+1 para StringDictionary... por cierto StringDictionaryno es lo mismo que Dictionary<string, string>cuando usas el constructor predeterminado.
Cheng Chen
ParallelExtensionsExtras @ code.msdn.microsoft.com/windowsdesktop/… contiene un ObservableConcurrentDictionary que es un excelente enlace de abeto y concurrencia.
VoteCoffee
3
increíble explicación, es realmente agradable que también hayas enumerado las similitudes para disminuir las preguntas que se te
ocurran
178

Porque Dictionaryes una clase genérica ( Dictionary<TKey, TValue>), de modo que el acceso a su contenido es de tipo seguro (es decir, no es necesario emitir desde Object, como lo hace con a Hashtable).

Comparar

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

a

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

Sin embargo, Dictionaryse implementa internamente como tabla hash, por lo que técnicamente funciona de la misma manera.

gius
fuente
88

FYI: en .NET, Hashtablees seguro para subprocesos para su uso por múltiples subprocesos de lectura y un solo subproceso de escritura, mientras que en Dictionarylos miembros estáticos públicos son seguros para subprocesos, pero no se garantiza que ningún miembro de instancia sea seguro para subprocesos.

HashtableDebido a esto, tuvimos que cambiar todos nuestros diccionarios a .

usuario38902
fuente
10
Divertido. El código fuente del Diccionario <T> se ve mucho más limpio y rápido. Puede ser mejor usar Dictionary e implementar su propia sincronización. Si las lecturas del Diccionario necesitan estar actualizadas, entonces simplemente tendría que sincronizar el acceso a los métodos de lectura / escritura del Diccionario. Sería mucho bloqueo, pero sería correcto.
Triynko
10
Alternativamente, si sus lecturas no tienen que ser absolutamente actuales, puede tratar el diccionario como inmutable. Luego, puede obtener una referencia al Diccionario y obtener rendimiento al no sincronizar las lecturas (ya que es inmutable e inherentemente seguro para subprocesos). Para actualizarlo, construye una copia completa actualizada del Diccionario en segundo plano, luego simplemente intercambia la referencia con Interlocked.CompareExchange (suponiendo un solo hilo de escritura; múltiples hilos de escritura requerirían sincronizar las actualizaciones).
Triynko
38
.Net 4.0 agregó la ConcurrentDictionaryclase que tiene todos los métodos públicos / protegidos implementados para ser seguros para subprocesos. Si no necesita admitir plataformas heredadas, esto le permitirá reemplazar el Hashtablecódigo multiproceso: msdn.microsoft.com/en-us/library/dd287191.aspx
Dan Is Fiddling By Firelight
anónimo al rescate. Buena respuesta.
unkulunkulu
55
Recuerdo haber leído que HashTable solo es seguro para subprocesos de lector-escritor en el escenario donde la información nunca se elimina de la tabla. Si un lector solicita un elemento que está en la tabla mientras se elimina un elemento diferente, y el lector buscaría el elemento en más de un lugar, es posible que mientras el lector está buscando, el escritor pueda mover el elemento de un lugar que no ha sido examinado a uno que sí lo ha hecho, lo que resulta en un informe falso de que el artículo no existe.
supercat
68

En .NET, la diferencia entre Dictionary<,>y HashTablees principalmente que el primero es un tipo genérico, por lo que obtienes todos los beneficios de los genéricos en términos de verificación de tipo estático (y boxeo reducido, pero esto no es tan grande como la gente tiende a pensar) términos de rendimiento: sin embargo, hay un costo de memoria definido para el boxeo).

Marc Gravell
fuente
34

La gente dice que un Diccionario es lo mismo que una tabla hash.

Esto no necesariamente es cierto. Una tabla hash es una forma de implementar un diccionario. Una típica en eso, y puede ser la predeterminada en .NET en la Dictionaryclase, pero no es, por definición, la única.

También podría implementar un diccionario utilizando una lista vinculada o un árbol de búsqueda, simplemente no sería tan eficiente (para algunas métricas de eficiente).

rix0rrr
fuente
44
Los documentos de MS dicen: "Recuperar un valor usando su clave es muy rápido, cercano a O (1), porque la clase Dictionary <(Of <(TKey, TValue>)>) se implementa como una tabla hash". - Por lo tanto, se le debe garantizar una tabla hash cuando se trata Dictionary<K,V>. IDictionary<K,V>podría ser cualquier cosa, aunque :)
snemarch
13
@ rix0rrr - Creo que lo tienes al revés, un Diccionario usa una HashTable, no una HashTable usa un Diccionario.
Joseph Hamilton
8
@JosephHamilton - rix0rrr lo hizo bien: "Una tabla hash es una implementación de un diccionario ". Se refiere al concepto "diccionario", no a la clase (tenga en cuenta las minúsculas). Conceptualmente, una tabla hash implementa una interfaz de diccionario. En .NET, Dictionary utiliza una tabla hash para implementar IDictionary. Es desordenado;)
Robert Hensing
Estaba hablando en .NET, ya que a eso se refería en su respuesta.
Joseph Hamilton
2
@JosephHamilton: implements (o implementación de ) ni siquiera remotamente significa lo mismo que los usos . Todo lo contrario. Quizás hubiera sido más claro si lo dijera de manera ligeramente diferente (pero con el mismo significado): "una tabla hash es una forma de implementar un diccionario". Es decir, si desea la funcionalidad de un diccionario, una forma de hacerlo (para implementar el diccionario) es utilizar una tabla hash.
ToolmakerSteve
21

Collections& Genericsson útiles para manejar grupos de objetos. En .NET, todos los objetos de colecciones vienen bajo la interfaz IEnumerable, que a su vez tiene ArrayList(Index-Value))& HashTable(Key-Value). Después de .NET Framework 2.0, ArrayList& HashTablefueron reemplazados por List& Dictionary. Ahora, los Arraylist& HashTableya no se utilizan en proyectos actuales.

Llegar a la diferencia entre HashTable& Dictionary, Dictionaryes genérico donde Hastableno es genérico. Podemos agregar cualquier tipo de objeto HashTable, pero al recuperarlo necesitamos convertirlo al tipo requerido. Por lo tanto, no es de tipo seguro. Pero dictionary, al declararse, podemos especificar el tipo de clave y valor, por lo que no es necesario emitir mientras se recupera.

Veamos un ejemplo:

Tabla de picadillo

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

    }
}

Diccionario,

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);
        }
    }
}
Sujit
fuente
2
en lugar de asignar explícitamente el tipo de datos para KeyValuePair, podríamos usar var. Por lo tanto, esto reduciría la escritura - foreach (var kv en dt) ... solo una sugerencia.
Ron
16

Diccionario:

  • Devuelve / arroja Excepción si intentamos encontrar una clave que no existe.

  • Es más rápido que un Hashtable porque no hay boxeo ni unboxing.

  • Solo los miembros estáticos públicos son seguros para subprocesos.

  • El diccionario es un tipo genérico, lo que significa que podemos usarlo con cualquier tipo de datos (al crear, debe especificar los tipos de datos para claves y valores).

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

  • Dictionay es una implementación segura de tipo de Hashtable, Keysy Valuesestá fuertemente tipada.

Tabla de picadillo:

  • Devuelve nulo si intentamos encontrar una clave que no existe.

  • Es más lento que el diccionario porque requiere boxeo y unboxing.

  • Todos los miembros de un Hashtable son seguros para subprocesos,

  • Hashtable no es un tipo genérico,

  • Hashtable es una estructura de datos de tipo libre, podemos agregar claves y valores de cualquier tipo.

Altaf Patel
fuente
"Devuelve / arroja Excepción si tratamos de encontrar una clave que no existe". No si lo usasDictionary.TryGetValue
Jim Balter
16

El extenso examen de las estructuras de datos utilizando el artículo de C # en MSDN afirma que también hay una diferencia en la estrategia de resolución de colisiones :

La clase Hashtable utiliza una técnica denominada rehashing .

Rehashing funciona de la siguiente manera: hay un conjunto de diferentes funciones hash, H 1 ... H n , y al insertar o recuperar un elemento de la tabla hash, inicialmente se utiliza la función hash H 1 . Si esto lleva a una colisión, H 2 se trataron en su lugar, y en adelante hasta H n si es necesario.

El Diccionario utiliza una técnica denominada encadenamiento .

Con rehashing, en caso de colisión, se vuelve a calcular el hash y se prueba la nueva ranura correspondiente a un hash. Con el encadenamiento, sin embargo, se utiliza una estructura de datos secundaria para contener cualquier colisión . Específicamente, cada espacio en el Diccionario tiene una matriz de elementos que se asignan a ese depósito. En el caso de una colisión, el elemento en colisión se antepone a la lista del depósito.

alexandrekow
fuente
16

Desde .NET Framework 3.5 también hay una HashSet<T>que proporciona todas las ventajas de Dictionary<TKey, TValue>si solo necesita las claves y ningún valor.

Entonces, si usa ay Dictionary<MyType, object>siempre establece el valor para nullsimular la tabla hash de tipo seguro, tal vez debería considerar cambiar a HashSet<T>.

Oliver
fuente
14

El Hashtablees una estructura de datos de tipo suelto, por lo que puede agregar claves y valores de cualquier tipo al Hashtable. La Dictionaryclase es una Hashtableimplementación de tipo seguro , y las claves y los valores están fuertemente tipados. Al crear una Dictionaryinstancia, debe especificar los tipos de datos para la clave y el valor.

carne
fuente
11

Observe que MSDN dice: "La clase Dictionary <(Of <(TKey, TValue>)>) se implementa como una tabla hash ", no "La clase Dictionary <(Of <(TKey, TValue>)>) se implementa como HashTable "

El diccionario NO se implementa como HashTable, pero se implementa siguiendo el concepto de una tabla hash. La implementación no está relacionada con la clase HashTable debido al uso de genéricos, aunque internamente Microsoft podría haber usado el mismo código y reemplazado los símbolos de tipo Object con TKey y TValue.

En .NET 1.0 Generics no existía; Aquí es donde HashTable y ArrayList comenzaron originalmente.

Brant
fuente
¿Puedes arreglar esa cita de MSDN? Algo falta o está mal; No es gramatical y algo incomprensible.
Peter Mortensen
10

Tabla de picadillo:

La clave / valor se convertirá en un tipo de objeto (boxeo) mientras se almacena en el montón.

La clave / valor debe convertirse al tipo deseado mientras se lee desde el montón.

Estas operaciones son muy costosas. Necesitamos evitar el boxeo / unboxing tanto como sea posible.

Diccionario: variante genérica de HashTable.

Sin boxeo / unboxing. No se requieren conversiones.

Siva Sankar Gorantla
fuente
8

Un objeto Hashtable consta de depósitos que contienen los elementos de la colección. Un depósito es un subgrupo virtual de elementos dentro de Hashtable, que hace que la búsqueda y recuperación sea más fácil y rápida que en la mayoría de las colecciones .

La clase Diccionario tiene la misma funcionalidad que la clase Hashtable. Un diccionario de un tipo específico (que no sea Object) tiene un mejor rendimiento que un Hashtable para los tipos de valor porque los elementos de Hashtable son de tipo Object y, por lo tanto, el encajonamiento y el desempaquetado ocurren típicamente si se almacena o recupera un tipo de valor.

Para leer más: Tipos de colección de diccionario y hash

mparkuk
fuente
7

Otra diferencia importante es que Hashtable es seguro para subprocesos. Hashtable tiene seguridad de subproceso de lector múltiple / escritor único (MR / SW) incorporado, lo que significa que Hashtable permite UN escritor junto con múltiples lectores sin bloqueo.

En el caso de Dictionary no hay seguridad de hilo; Si necesita seguridad de subprocesos, debe implementar su propia sincronización.

Para elaborar más a fondo:

Hashtable proporciona cierta seguridad de subprocesos a través de la Synchronizedpropiedad, que devuelve un contenedor seguro para subprocesos alrededor de la colección. El contenedor funciona bloqueando toda la colección en cada operación de agregar o quitar. Por lo tanto, cada subproceso que intenta acceder a la colección debe esperar su turno para tomar el único bloqueo. Esto no es escalable y puede causar una degradación significativa del rendimiento para grandes colecciones. Además, el diseño no está completamente protegido de las condiciones de carrera.

Las clases de colección de .NET Framework 2.0 como List<T>, Dictionary<TKey, TValue>, etc. no proporcionan ninguna sincronización de subprocesos; el código de usuario debe proporcionar toda la sincronización cuando se agregan o eliminan elementos en varios subprocesos simultáneamente

Si necesita seguridad de tipo y seguridad de subprocesos, use clases de colecciones concurrentes en .NET Framework. Lectura adicional aquí .

Una diferencia adicional es que cuando agregamos las entradas múltiples en el Diccionario, se mantiene el orden en que se agregan las entradas. Cuando recuperemos los elementos del Diccionario, obtendremos los registros en el mismo orden en que los insertamos. Mientras que Hashtable no conserva el orden de inserción.

Referencia nula
fuente
Por lo que entiendo, Hashsetgarantiza la seguridad de la rosca MR / SW en escenarios de uso que no implican eliminaciones . Creo que podría haber sido completamente seguro para MR / SW, pero manejar las eliminaciones de manera segura aumenta en gran medida el gasto de seguridad de MR / SW. Si bien el diseño de Dictionarypodría haber ofrecido seguridad MR / SW a un costo mínimo en escenarios sin eliminación, creo que MS quería evitar tratar los escenarios sin eliminación como "especiales".
supercat
5

Una diferencia más que puedo entender es:

No podemos usar Diccionario <KT, VT> (genéricos) con servicios web. La razón es que ningún estándar de servicio web admite el estándar genérico.

Peter Mortensen
fuente
Podemos usar listas genéricas (List <string>) en un servicio web basado en jabones. Pero no podemos usar el diccionario (o tabla hash) en un servicio web. Creo que la razón de esto es que .net xmlserializer no puede manejar objetos de diccionario.
Siddharth
5

Dictionary<> es un tipo genérico y, por lo tanto, es de tipo seguro.

Puede insertar cualquier tipo de valor en HashTable y esto a veces puede generar una excepción. Pero Dictionary<int>solo aceptará valores enteros y de manera similar Dictionary<string>solo aceptará cadenas.

Por lo tanto, es mejor usar en Dictionary<>lugar de HashTable.

Kishore Kumar
fuente
0

En la mayoría de los lenguajes de programación, los diccionarios son preferibles a las tablas hash

No creo que esto sea necesariamente cierto, la mayoría de los idiomas tienen uno u otro, dependiendo de la terminología que prefieran .

En C #, sin embargo, la razón clara (para mí) es que C # HashTables y otros miembros del sistema. Los espacios de nombres de las colecciones son en gran parte obsoletos. Estaban presentes en c # V1.1. Han sido reemplazados de C # 2.0 por las clases genéricas en el espacio de nombres System.Collections.Generic.

kristianp
fuente
Una de las ventajas de una tabla hash sobre un diccionario es que si una clave no existe en un diccionario, arrojará un error. Si una clave no existe en una tabla hash, solo devuelve nulo.
Bill Norman
En C # todavía evitaría usar System.Collections.Hashtable ya que no tienen la ventaja de los genéricos. Puede usar TryGetValue o HasKey de Dictionary si no sabe si la clave existirá.
Kristianp
Vaya, no HasKey, debería ser ContainsKey.
kristianp
-3

Según lo que veo 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;

Por lo tanto, podemos estar seguros de que DictionaryBase utiliza una HashTable internamente.

Peter Mortensen
fuente
16
System.Collections.Generic.Dictionary <TKey, TValue> no se deriva de DictionaryBase.
snemarch
"Por lo tanto, podemos estar seguros de que DictionaryBase utiliza una HashTable internamente". - Eso está bien, pero no tiene nada que ver con la pregunta.
Jim Balter