2010-07-04 18 views
11

Estoy muy confundido por el nombre 'unordered_map'. El nombre sugiere que las claves no están ordenadas en absoluto. Pero siempre pensé que estaban ordenados por su valor hash. ¿O es incorrecto (porque el nombre implica que no están ordenados)?¿El mapa sin ordenar es realmente desordenado?

O, para decirlo diferente: ¿Es esta

typedef map<K, V, HashComp<K> > HashMap; 

con

template<typename T> 
struct HashComp { 
    bool operator<(const T& v1, const T& v2) const { 
     return hash<T>()(v1) < hash<T>()(v2); 
    } 
}; 

lo mismo que

typedef unordered_map<K, V> HashMap; 

? (Bueno, no exactamente, STL se quejará aquí porque puede haber claves k1, k2 y tampoco k2 k1 < ni < k1 k2 Usted tendría que utilizar multimap y sobrescribir la igualdad de verificación..)

O también de otra manera: Cuando los repito, ¿puedo suponer que la lista de claves está ordenada por su valor hash?

+0

duplicado Posible de http: //stackoverflow.com/questions/3039823/boostunordered-map-is-ordered – Cogwheel

Respuesta

19

En respuesta a su pregunta editada, no esos dos fragmentos no son equivalentes en absoluto. std::map almacena nodos en una estructura de árbol, unordered_map los almacena en una tabla hash *.

Las claves no se almacenan en orden de su "valor hash" porque no están almacenadas en ninguna orden en absoluto. En cambio, se almacenan en "cubos" donde cada cubeta corresponde a un rango de valores hash. Básicamente, la implementación es el siguiente:

function add_value(object key, object value) { 
    int hash = key.getHash(); 

    int bucket_index = hash % NUM_BUCKETS; 
    if (buckets[bucket_index] == null) { 
     buckets[bucket_index] = new linked_list(); 
    } 
    buckets[bucket_index].add(new key_value(key, value)); 
} 

function get_value(object key) { 
    int hash = key.getHash(); 

    int bucket_index = hash % NUM_BUCKETS; 
    if (buckets[bucket_index] == null) { 
     return null; 
    } 

    foreach(key_value kv in buckets[bucket_index]) { 
     if (kv.key == key) { 
      return kv.value; 
     } 
    } 
} 

Obviamente que es una simplificación grave e implementación real sería mucho más avanzados (por ejemplo, el apoyo a cambiar el tamaño de la matriz buckets, tal vez usando una estructura de árbol en lugar de lista enlazada para los cucharones , y así sucesivamente), pero eso debería dar una idea de cómo no puede recuperar los valores en un orden en particular. Ver wikipedia para más información.


* Técnicamente, la implementación interna de std::map y unordered_map son definido por la implementación, pero el estándar requiere cierta complejidad Big-O para las operaciones que implica esas implementaciones internas

+1

De lejos, la mejor respuesta. – Wizard79

+1

Muchas gracias. Eso realmente lo aclara. Siempre pensé que una tabla hash se implementaría internamente usando una estructura de árbol (como un mapa de valores de hash a cubos). Parece que estaba terriblemente equivocado allí. – Albert

+1

Esto fue downvoted nuevamente por al menos alguien. ¿Qué es todo esto de downvoting aquí? ¿Pueden las personas que menosprecian algo dar algunos comentarios? – Albert

1

Si quieres una analogía, mira el RDBMS que elijas.

Si no especifica una cláusula ORDER BY al realizar una consulta, los resultados se devuelven "desordenados", es decir, en el orden en que se encuentre la base de datos. La orden no está especificada, y el sistema es libre de "ordenarlos" como quiera para obtener el mejor rendimiento.

+1

¿Están realmente desordenados? ¿No saldrían ordenados por el valor hash? – Albert

+0

No me gusta esa analogía, porque en unordered_map el orden no es un detalle interno oscuro, sino que en realidad es la consecuencia del algoritmo hash. De hecho * si tiene una función hash óptima, el número de operaciones realizadas durante la búsqueda, inserción y eliminación de un elemento arbitrario no depende del número de elementos en la secuencia * (http://tiny.cc/vqm58) – Wizard79

1

Tienes razón, unordered_map es en realidad hash ordenado. Tenga en cuenta que la mayoría de las implementaciones actuales (pre TR1) lo llaman hash_map.

El IBM C/C++ documentation comentarios que si tiene una función de dispersión óptima, el número de operaciones realizadas durante las operaciones de búsqueda, inserción y eliminación de un elemento arbitrario no depende del número de elementos en la secuencia , entonces esto significa que la orden no está tan desordenada ...

Ahora, ¿qué significa que es hash pedido? Como un hash debe ser impredecible, por definición no puede asumir ningún supuesto sobre el orden de los elementos en el mapa. Esta es la razón por la que ha sido renombrado en TR1: el antiguo nombre sugería una orden. Ahora sabemos que realmente se usa un pedido, pero puede ignorarlo ya que es impredecible.

+2

Eh, ¿por qué fue esto downvoted? Eso me pareció hasta ahora la respuesta más correcta. ¿No es así? Por favor, aquellos que no lo creen, agreguen algunos comentarios. – Albert

+0

Ver las otras respuestas. Una implementación muy común ordena las claves mediante 'hash (Key)% NumberOfBuckets', que definitivamente no es lo mismo que pedir' hash (Key) '. Una de las consecuencias importantes es que la orden puede cambiar si se insertan más elementos y crece la cantidad de depósitos. Si supone incorrectamente que fue ordenado por hash, la orden no cambiaría si agrega más elementos. – MSalters

+0

@MSalters: es por eso que escribí que no debes confiar en ninguna orden de hash ya que es impredecible. – Wizard79

6

"Desordenado" no significa que no haya una secuencia lineal en algún lugar de la implementación. Significa que "no se puede asumir nada sobre el orden de estos elementos".

Por ejemplo, las personas suelen suponer que las entradas saldrán de un mapa hash en el mismo orden en que se colocaron. Pero no es así, porque las entradas no están ordenadas.

En cuanto a "ordenado por su valor hash": los valores hash generalmente se toman de la gama completa de enteros, pero los mapas hash no tienen 2 ** 32 ranuras en ellos. El rango del valor de hash se reducirá al número de slots tomando el módulo de la cantidad de slots. Además, al agregar entradas a un mapa hash, es posible que cambie el tamaño para acomodar los nuevos valores. Esto puede causar que todas las entradas anteriores sean reubicadas, cambiando su orden.

En una estructura de datos desordenada, no se puede asumir nada sobre el orden de las entradas.

+0

Pensé que puedo suponer que salen ordenadas por su valor de hash. – Albert

+0

He agregado más ... –

+0

Sí, claro, pero aún así se ordenarían por su valor hash. Por supuesto, si el valor hash es el mismo para diferentes claves, el orden no está definido. – Albert

2

Como sugiere el nombre unordered_map, el orden en C++ 0x no especifica ningún orden. El orden aparente de unordered_map dependerá de lo que sea conveniente para la implementación real.

+0

¿Por qué es eso así? ¿No es obvio ordenar por valor hash? – Albert

+1

@Albert Nada dice que un mapa no ordenado debe usar hash. Y, de hecho, cuando se tienen en cuenta las colisiones, el orden de un mapa no ordenado no es predecible desde una función hash. –

+0

@Albert: es para que los implementadores decidan el mejor orden que se ajuste a su implementación. unordered_map no * garantiza * ningún pedido, usted no confía en él, los implementadores deciden el mejor orden (si lo hay) para ofrecer el mejor rendimiento; El final de la historia. Está en el espíritu del estándar C++ exigir el mínimo indispensable y evitar restricciones inútiles para permitir que los implementadores proporcionen el mejor rendimiento posible. –

Cuestiones relacionadas