Per espandere la risposta di David Richerby, il termine " funzione hash " è un po 'sovraccarico. Spesso, quando parliamo di una funzione hash, pensiamo a MD5, SHA-1 o qualcosa come il .hashCode()
metodo Java , che trasforma alcuni input in un singolo numero. Tuttavia, è improbabile che il dominio di questo numero (ovvero il valore massimo) abbia le stesse dimensioni della tabella hash in cui si sta tentando di archiviare i dati. (MD5 è 16 byte, SHA-1 è 20 byte ed .hashCode()
è un int
- 4 byte).
Quindi la tua domanda riguarda il prossimo passo: una volta che abbiamo una funzione hash in grado di mappare input arbitrari su numeri, come li inseriamo in una struttura di dati di una dimensione particolare? Con un'altra funzione, chiamata anche "funzione hash"!
Un banale esempio di tale funzione è il modulo ; puoi facilmente mappare un numero di dimensioni arbitrarie su un indice specifico in un array con modulo. Questo è introdotto in CLRS come "metodo di divisione":
Nel metodo di divisione per la creazione di funzioni hash, mappiamo una chiave in uno degli slot prendendo il resto di diviso per . Cioè, la funzione hash èkmkm
h(k)=k mod .m
...
Quando si utilizza il metodo di divisione di solito si evitano determinati valori di . Ad esempio, non dovrebbe essere una potenza di 2, poiché se allora è solo i bit di ordine inferiore di .mmm=2ph(k)pk
~ Introduzione agli algoritmi, §11.3.1 - CLRS
Quindi modulo non è una grande funzione di hash, poiché limita le dimensioni che possiamo utilizzare in sicurezza per la nostra struttura di dati sottostante. La sezione successiva introduce un "metodo di moltiplicazione" leggermente più complesso, che utilizza anche il modulo ma è vantaggioso perché "il valore di non è critico". Funziona comunque meglio con alcune conoscenze preliminari di "caratteristiche dei dati sottoposti a hash" - qualcosa che spesso non conosciamo.m
Java HashMap
utilizza una versione modificata del metodo di divisione che esegue una fase di pre-elaborazione per tenere conto delle .hashCode()
implementazioni deboli in modo da poter utilizzare array di potenza di due dimensioni. Puoi vedere esattamente cosa sta succedendo nel .getEntry()
metodo (i commenti sono miei):
// hash() transforms key.hashCode() to protect against bad hash functions
int hash = (key == null) ? 0 : hash(key.hashCode());
// indexOf() converts the resulting hash to a value between 0 and table.length-1
for (Entry<K,V> e = table[indexFor(hash, table.length)];
...
Java 8 ha portato con sé una riscrittura HashMap
ancora più veloce, ma un po 'più difficile da leggere. Tuttavia, utilizza lo stesso principio generale per la ricerca dell'indice.