2010-07-04 18 views
11

Sono molto confuso dal nome "unordered_map". Il nome suggerisce che le chiavi non sono affatto ordinate. Ma ho sempre pensato che fossero ordinati per il loro valore hash. O è sbagliato (perché il nome implica che non sono ordinati)?La carta non ordinata è davvero non ordinata?

O per dirla diverso: È questo

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 stesso

typedef unordered_map<K, V> HashMap; 

? (OK, non esattamente, STL si lamenterà qui perché ci possono essere le chiavi k1, k2 e né k1 < k2 né k2 < k1 Lei avrebbe bisogno di utilizzare multimap e sovrascrivere il pari-check..)

O ancora in modo diverso: Quando li elabora, posso supporre che la key-list sia ordinata dal loro valore hash?

+0

Eventuali duplicati di http: //stackoverflow.com/questions/3039823/boostunordered-map-is-ordered – Cogwheel

risposta

19

In risposta alla domanda modificata, questi due frammenti non sono affatto equivalenti. std::map memorizza i nodi in una struttura ad albero, unordered_map li memorizza in una tabella hash *.

Le chiavi non vengono memorizzate in base al loro "valore hash" perché non vengono memorizzate in in qualsiasi ordine. Sono invece memorizzati in "bucket" in cui ciascun bucket corrisponde a un intervallo di valori hash. In sostanza, l'applicazione è questa:

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

Ovviamente questa è una grave semplificazione e reale attuazione sarebbe molto più avanzato (ad esempio, sostenendo ridimensionamento della matrice buckets, magari usando una struttura ad albero anziché lista collegata per le benne , e così via), ma ciò dovrebbe dare un'idea di come non è possibile recuperare i valori in un ordine particolare. Vedere wikipedia per ulteriori informazioni.


* Tecnicamente, l'implementazione interna di std::map e unordered_map sono definiti dall'implementazione, ma lo standard richiede certa complessità O-grande per le operazioni che implica quelle implementazioni interne

+1

Di gran lunga la migliore risposta. – Wizard79

+1

Grazie mille. Questo lo chiarisce davvero. Ho sempre pensato che un hashtable sarebbe stato implementato internamente usando una struttura ad albero (proprio come una mappa dai valori hash ai bucket). Sembra che mi sia terribilmente sbagliato. – Albert

+1

Questo è stato downvoted di nuovo da almeno qualcuno. Cos'è tutto questo downvoting qui? Quelle persone che non valgono nulla possono dare qualche commento? – Albert

1

Se si desidera un'analogia, controllare l'RDBMS desiderato.

Se non si specifica una clausola ORDER BY quando si esegue una query, i risultati vengono restituiti "non ordinati", ovvero in qualsiasi ordine si desideri il database. L'ordine non è specificato e il sistema è libero di "ordinarli", ma a suo piacimento per ottenere le migliori prestazioni.

+1

Sono davvero non ordinate? Non sarebbero usciti ordinati per il valore hash? – Albert

+0

Non mi piace l'analogia, perché in unordered_map l'ordine non è un oscuro dettaglio interno, ma in realtà è la conseguenza dell'algoritmo hash. Infatti * se si dispone di una funzione hash ottimale, il numero di operazioni eseguite durante la ricerca, l'inserimento e la rimozione di un elemento arbitrario non dipende dal numero di elementi nella sequenza * (http://tiny.cc/vqm58) – Wizard79

1

Avete ragione, unordered_map è in realtà hash ordinato. Nota che la maggior parte delle implementazioni attuali (pre TR1) lo chiamano hash_map.

IBM C/C++ compilatore documentation osservazioni che se si dispone di una funzione ottimale hash, il numero di operazioni eseguite durante la ricerca, l'inserimento e la rimozione di un elemento arbitrario non dipende dal numero di elementi nella sequenza , quindi questo significa che l'ordine non è così non ordinato ...

Ora, che cosa significa che è hash ordinato? Poiché un hash dovrebbe essere imprevedibile, per definizione non puoi assumere alcuna ipotesi sull'ordine degli elementi nella mappa. Questo è il motivo per cui è stato rinominato in TR1: il vecchio nome suggeriva un ordine. Ora sappiamo che un ordine è effettivamente utilizzato, ma puoi ignorarlo perché è imprevedibile.

+2

Eh, perché è stato downvoted? Mi è sembrata finora la risposta più corretta. Non è vero? Per favore quelli che non pensano che sia, aggiungi qualche commento. – Albert

+0

Vedere le altre risposte. Un'implementazione molto comune ordina le chiavi con 'hash (Key)% NumberOfBuckets', che non è assolutamente lo stesso dell'ordine di' hash (Key) '. Una delle conseguenze importanti è che l'ordine può cambiare se vengono inseriti più elementi e il numero di contenitori aumenta. Se si suppone erroneamente che sia stato ordinato hash, l'ordine non cambierà se si aggiungono più elementi. – MSalters

+0

@MSalters: è per questo che ho scritto che non devi fare affidamento su alcun ordine hash in quanto è imprevedibile. – Wizard79

6

"Non ordinato" non significa che non ci sia una sequenza lineare da qualche parte nell'implementazione. Significa "non puoi assumere nulla sull'ordine di questi elementi".

Ad esempio, le persone spesso presumono che le voci escano da una mappa hash nello stesso ordine in cui sono state inserite. Ma non lo fanno, perché le voci non sono ordinate.

Come per "ordinato dal loro valore di hash": i valori di hash sono generalmente presi dall'intero intervallo di interi, ma le mappe di hash non hanno 2 ** 32 slot in essi. L'intervallo del valore hash verrà ridotto al numero di slot assumendolo modulo il numero di slot. Inoltre, man mano che aggiungi voci a una mappa hash, è possibile che le dimensioni cambino per adattarsi ai nuovi valori. Ciò può causare la reinserzione di tutte le voci precedenti, modificandone l'ordine.

In una struttura di dati non ordinata, non è possibile assumere nulla sull'ordine delle voci.

+0

Ho pensato di poter supporre che escano ordinati per il loro valore hash. – Albert

+0

Ho aggiunto altro ... –

+0

Sì, certo, ma sarebbero comunque ordinati in base al loro valore hash. Naturalmente se il valore hash è lo stesso per chiavi diverse, l'ordine non è definito. – Albert

2

Come suggerisce il nome unordered_map, nessun ordinamento è specificato dallo standard C++ 0x. L'apparente ordinamento di unordered_map dipenderà da ciò che è conveniente per l'implementazione effettiva.

+0

Perché è così? Non è ovvio ordinare per valore hash? – Albert

+1

@Albert Nulla dice che unordered_map deve usare l'hashing. E infatti quando vengono prese in considerazione le collisioni, l'ordine di una mappa non ordinata non è prevedibile da una funzione di hash. –

+0

@Albert: è così che gli implementatori decidano il miglior ordine che si adatta alla loro implementazione. unordered_map non * garantisce * alcun ordine, non ci si basa su di esso, gli implementatori decidono l'ordine migliore (se presente) per fornire le migliori prestazioni; fine della storia. È nello spirito dello standard C++ richiedere il minimo indispensabile ed evitare inutili vincoli per consentire agli implementatori di fornire le migliori prestazioni possibili. –

Problemi correlati