web-dev-qa-db-fra.com

Quelle fonction de hachage Java utilise-t-il pour implémenter la classe Hashtable?

Dans le livre CLRS ("Introduction to Algorithms"), il existe plusieurs fonctions de hachage, telles que mod, multiply, etc. 

Quelle fonction de hachage Java utilise-t-il pour mapper les clés sur les emplacements?

J'ai vu qu'il y avait une question ici Fonction de hachage utilisée en langage Java . Mais cela ne répond pas à la question et je pense que la réponse marquée à cette question est fausse. Il dit que hashCode () vous permet de créer votre propre fonction de hachage pour Hashtable, mais je pense que c'est faux.

L'entier renvoyé par hashCode () est la clé réelle de Hashtble, puis Hashtable utilise une fonction de hachage pour hacher hashCode (). Ce que cette réponse implique, c'est que Java vous donne une chance de donner à Hashtable une fonction de hachage, mais non, c'est faux. hashCode () donne la vraie clé, pas la fonction de hachage.

Alors, quelle est exactement la fonction de hachage utilisée par Java?

47
Jackson Tale

Lorsqu'une clé est ajoutée ou demandée à un HashMap dans OpenJDK, le flux d'exécution est le suivant:

  1. La clé est transformée en une valeur 32 bits à l'aide de la méthode hashCode() définie par le développeur.
  2. La valeur 32 bits est ensuite transformée par une seconde fonction de hachage (dont la réponse d'Andrew contient le code source) en un décalage à l'intérieur de la table de hachage. Cette deuxième fonction de hachage est fournie par l’implémentation de HashMap et ne peut pas être remplacée par le développeur.
  3. L'entrée correspondante de la table de hachage contient une référence à une liste liée ou null, si la clé n'existe pas encore dans la table de hachage. S'il y a des collisions (plusieurs clés avec le même décalage), les clés ainsi que leurs valeurs sont simplement rassemblées dans une liste chaînée.

Si la taille de la table de hachage a été choisie de manière appropriée, le nombre de collisions sera limité. Ainsi, une seule recherche ne prend que du temps constant en moyenne. Ceci est appelé temps constant attendu. Cependant, si un attaquant contrôle les clés insérées dans une table de hachage et connaît l'algorithme de hachage utilisé, il peut provoquer de nombreuses collisions de hachage et, par conséquent, forcer le temps de recherche linéaire. C'est pourquoi certaines implémentations de tables de hachage ont été récemment modifiées pour inclure un élément aléatoire rendant plus difficile pour un attaquant de prédire quelles clés provoqueront des collisions.

Certains ASCII art

key.hashCode()
     |
     | 32-bit value
     |                              hash table
     V                            +------------+    +----------------------+
HashMap.hash() --+                | reference  | -> | key1 | value1 | null |
                 |                |------------|    +----------------------+
                 | modulo size    | null       |
                 | = offset       |------------|    +---------------------+
                 +--------------> | reference  | -> | key2 | value2 | ref |
                                  |------------|    +---------------------+
                                  |    ....    |                       |
                                                      +----------------+
                                                      V
                                                    +----------------------+
                                                    | key3 | value3 | null |
                                                    +----------------------+
91
Niklas B.

Selon la source de hashmap, chaque hashCode est haché à l'aide de la méthode suivante:

 /**
 * Applies a supplemental hash function to a given hashCode, which
 * defends against poor quality hash functions.  This is critical
 * because HashMap uses power-of-two length hash tables, that
 * otherwise encounter collisions for hashCodes that do not differ
 * in lower bits. Note: Null keys always map to hash 0, thus index 0.
 */
static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

La raison pour laquelle chaque hashCode est à nouveau haché est pour éviter toute collision (voir les commentaires ci-dessus)

HashMap utilise également une méthode pour déterminer l'index d'un code de hachage (la longueur étant toujours une puissance de 2, vous pouvez utiliser & au lieu de%):

/**
 * Returns index for hash code h.
 */
static int indexFor(int h, int length) {
    return h & (length-1);
}

La méthode put ressemble à quelque chose comme:

int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);

Le but d'un code de hachage est de fournir une représentation entière unique pour un objet donné. Il est donc logique que la méthode hashCode d’Integer renvoie simplement la valeur, car chaque valeur serait unique pour cet objet Integer.

27
Andrew Liu

Le hachage en général est divisé en deux étapes: A. HashCode B. Compression

À l'étape a. un entier correspondant à votre clé est généré. Cela peut être modifié par vous en Java.

À l'étape b. Java applique une technique de compression pour mapper le nombre entier renvoyé par l'étape a. à un emplacement dans le hashmap ou hashtable. Cette technique de compression ne peut pas être modifiée.

4
Srikant Aggarwal

Je pense qu'il y a une certaine confusion à propos du concept ici. Une fonction de hachage mappe une entrée de taille variable sur une sortie de taille fixe (la valeur de hachage). Dans le cas d'objets Java, la sortie est un entier signé de 32 bits.

Hashtable de Java utilise la valeur de hachage comme index dans un tableau dans lequel l'objet réel est stocké, en tenant compte de l'arithmétique modulo et des collisions. Cependant, ce n'est pas hachage.

L'implémentation Java.util.HashMap effectue un échange de bits supplémentaire sur la valeur de hachage avant l'indexation afin de se protéger contre les collisions excessives dans certains cas. C'est ce qu'on appelle "un hachage supplémentaire", mais je ne pense pas que ce soit un terme correct.

0
forty-two
/**
 * Computes key.hashCode() and spreads (XORs) higher bits of hash
 * to lower.  Because the table uses power-of-two masking, sets of
 * hashes that vary only in bits above the current mask will
 * always collide. (Among known examples are sets of Float keys
 * holding consecutive whole numbers in small tables.)  So we
 * apply a transform that spreads the impact of higher bits
 * downward. There is a tradeoff between speed, utility, and
 * quality of bit-spreading. Because many common sets of hashes
 * are already reasonably distributed (so don't benefit from
 * spreading), and because we use trees to handle large sets of
 * collisions in bins, we just XOR some shifted bits in the
 * cheapest possible way to reduce systematic lossage, as well as
 * to incorporate impact of the highest bits that would otherwise
 * never be used in index calculations because of table bounds.
 */
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

Il s'agit de la dernière fonction de hachage utilisée par la classe hashMap en Java.

0
Avinash