Pour développer la réponse de David Richerby, le terme " fonction de hachage " est un peu surchargé. Souvent, lorsque nous parlons d'une fonction de hachage, nous pensons à MD5, SHA-1, ou quelque chose comme la .hashCode()
méthode Java , qui transforme certaines entrées en un seul nombre. Cependant, le domaine de ce nombre (c'est-à-dire la valeur maximale) a très peu de chances d'être de la même taille que la table de hachage dans laquelle vous essayez de stocker des données. (MD5 est de 16 octets, SHA-1 est de 20 octets et .hashCode()
est un int
- 4 octets).
Votre question porte donc sur la prochaine étape - une fois que nous avons une fonction de hachage qui peut mapper des entrées arbitraires sur des nombres, comment les placer dans une structure de données d'une taille particulière? Avec une autre fonction, également appelée "fonction de hachage"!
Un exemple trivial d'une telle fonction est modulo ; vous pouvez facilement mapper un certain nombre de tailles arbitraires à un index spécifique dans un tableau avec modulo. Ceci est introduit dans CLRS comme "la méthode de division":
Dans la méthode de division pour créer des fonctions de hachage, nous mappons une clé dans l'un des emplacements en prenant le reste de divisé par . Autrement dit, la fonction de hachage estkmkm
h(k)=k mod .m
...
Lors de l'utilisation de la méthode de division, nous évitons généralement certaines valeurs de . Par exemple, ne devrait pas être une puissance de 2, car si alors n'est que les bits de poids faible de .m m = 2 p h ( k ) p kmmm=2ph(k)pk
~ Introduction aux algorithmes, §11.3.1 - CLRS
Le modulo n'est donc pas une excellente fonction de hachage, car il limite les tailles que nous pouvons utiliser en toute sécurité pour notre structure de données sous-jacente. La section suivante présente une "méthode de multiplication" légèrement plus complexe, qui utilise également le modulo mais est avantageuse car "la valeur de n'est pas critique". Cependant, cela fonctionne mieux avec une connaissance préalable des «caractéristiques des données hachées» - quelque chose que nous ne savons souvent pas.m
Java HashMap
utilise une version modifiée de la méthode de division qui effectue une étape de prétraitement pour tenir compte des .hashCode()
implémentations faibles afin de pouvoir utiliser des tableaux de taille deux. Vous pouvez voir exactement ce qui se passe dans la .getEntry()
méthode (les commentaires sont les miens):
// 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 a apporté une réécriture HashMap
qui est encore plus rapide, mais un peu plus difficile à lire. Il utilise cependant le même principe général pour la recherche d'index.