Die Rolle der Hash-Methode in HashMap (ausführliche Erklärung)

Zunächst einmal: Wofür wird die Hash-Methode verwendet?

Bevor wir das Prinzip verstehen, stellen wir uns zunächst auf die Schultern der Giganten und verstehen kurz die wesentliche Funktion der Hash-Methode.

Im Wesentlichen ist seine Funktion sehr einfach, nämlich den Schlüsselwert zu verwenden, um auf irgendeine Weise einen Hash-Code zu berechnen.

Und dieser Hash-Code wird später verwendet, um den Index des zugrunde liegenden Arrays zu berechnen, in dem der Schlüssel vorhanden ist. Daher muss ein gewisser Grad an Zufälligkeit beibehalten werden, um die berechneten Array-Element-Indizes gleichmäßiger zu verteilen und Kollisionen zu reduzieren, was eigentlich dazu dient, sie zu vermeiden in einem größeren Ausmaß. Hash-Konflikt.

Hinweis: Die Eigenschaft der Aufrechterhaltung der Zufälligkeit spiegelt sich nur im Schritt der Berechnung des Hash-Werts mithilfe der Hash-Methode wider. Die anschließende Modulo-Operation dient nur der Ermittlung des Rests und hat nichts mit der Sicherstellung der Zufälligkeit zu tun.

Was ist die Hash-Methode?

Hier ist der Quellcode intuitiv:

Parameterschlüssel : Der Schlüsselwert, für den der Hash-Code berechnet werden muss .
key == null ? 0 : (h = key.hashCode()) ^ (h >>> 16) : Dies ist ein ternärer Operator, der kompliziert erscheint, aber eigentlich leicht zu verstehen ist.
Die Logik lautet: Wenn der Schlüsselwert null ist, ist der Hash-Code 0 (was immer noch bedeutet, dass, wenn der Schlüssel null ist , er an der ersten Position gespeichert wird); andernfalls wird der Hash-Code des Schlüssels durch Aufrufen von hashCode abgerufen () -Methode, und es wird XOR-verknüpft, wobei der Hash-Code um 16 Bit nach rechts verschoben wird.
^ Operator: Der XOR-Operator ist ein bitweiser Operator in Java . Er wird verwendet, um die binären Bits zweier Zahlen zu vergleichen. Wenn sie gleich sind, sind sie 0, und wenn sie unterschiedlich sind, sind sie 1 .
 >>> 16 : Verschieben Sie den Hash-Code um 16 Bit nach rechts, was einer Aufteilung des ursprünglichen Hash-Codes in zwei 16 -Bit-Teile entspricht. Was schließlich zurückgegeben wird, ist der Hash-Codewert, der nach der XOR-Operation erhalten wird.

Diese kurze Codezeile vereint den Einfallsreichtum vieler Computergiganten.

Theoretisch ist der Hash-Wert (dh der von der Hashcode-Methode zurückgegebene Wert) ein int- Typ. Jeder weiß, dass der int-Typ in Java 4 Bytes, also 32 Bits, im Bereich von -2147483648 bis 2147483648 belegt . Der gesamte Zuordnungsraum beträgt etwa 4 Milliarden. Solange die Hash-Werte relativ gleichmäßig und locker zugeordnet werden, kommt es im Allgemeinen zu keinen Hash-Kollisionen (Hash-Konflikte verringern die Effizienz von HashMap).
Das Problem besteht jedoch darin, dass ein Array mit einer Länge von 4 Milliarden nicht in den Speicher passt. Die anfängliche Größe des Arrays vor der HashMap- Erweiterung betrug nur 16 , daher kann dieser Hash-Wert nicht direkt verwendet werden. Bevor Sie ihn verwenden, müssen Sie eine Modulo-Operation mit der Länge des Arrays ((n - 1) & Hash wie oben erwähnt) durchführen. , und Sie können den Rest verwenden, um auf den Array-Index zuzugreifen.

Wie ist h ^ h>>>16 zu verstehen?

Das h oben ist eigentlich der Hashcode-Wert, den wir durch Aufrufen der hashcode()-Methode des Schlüssels erhalten. Wir verschieben ihn um 16 Bit nach rechts (da es sich um einen int-Typ handelt, also insgesamt 32 Bit) und füllen den ersten Wert aus 16 Bits mit 0, und dann machen wir es mit dem Original gleich. Der Hashcode-Wert wird XOR-verknüpft, da sich die Logik von XOR von 1 unterscheidet, was den ersten 16 Bits entspricht, und die letzten 16 Bits werden einmal XOR-verknüpft und belegen Die 16 aufgefüllten Nullen werden XOR-verknüpft und belegen die ersten 16 Bits des endgültig zurückgegebenen Hash. Der endgültig zurückgegebene Hash-Code wird erhalten.

Der obige Prozess kann anhand der folgenden Abbildung betrachtet werden:

 

Auf diese Weise sind Sie möglicherweise verwirrt. Was ist der Grund für dieses Hin- und Hergehen? Tatsächlich hat alles eine Sache: die Zufälligkeit des Hash-Codes, und der Zweck besteht darin, Hash-Konflikte zu vermeiden. Schließlich Hash-Konflikte verringern die Effizienz von HashMap.

Zusammenfassend lässt sich sagen, dass das Hash- Verfahren zur Optimierung des Hash-Werts verwendet wird . Es verschiebt den Hash-Wert um 16 Bit nach rechts , was genau der Hälfte seiner Länge entspricht, und führt dann eine XOR-Operation mit dem ursprünglichen Hash-Wert durch und vermischt ihn so . Die High- und Low-Bits im ursprünglichen Hash-Wert erhöhen die Zufälligkeit.

 

Wie verwenden wir nach der Berechnung des Hash-Werts den Hash-Wert zur Berechnung des Index?

Wir verwenden (n - 1) & Hash, um den Index zu erhalten; (eigentlich ist es eine Modulo-Operation)

Sie sind vielleicht neugierig: Sollten Sie nicht % verwenden, um Modulo-Operationen auszuführen? Warum die bitweise Operation & verwenden ?

Dies liegt daran, dass % zwar tatsächlich erreicht werden kann, der tatsächliche Wirkungsgrad jedoch immer noch nicht so gut ist wie &

Und unser & kann % ersetzen, es gibt eine Voraussetzung:

Nur wenn b 2 in der n-ten Potenz ist , gibt es eine solche Formel: a % b = a & (b-1 )

Wir können es überprüfen:

Lassen Sie es uns überprüfen, wenn a = 14 , b = 8 , also 2^3 , n=3 .
14%8 (Rest ist 6), das Binärsystem von 14 ist 1110 , das Binärsystem von 8 ist 1000 , 8-1 = 7 , das Binärsystem von 7 ist 0111 , 1110&0111=0110 , also 0 * 2^ 0+1 * 2^1+ 1 * 2^2+0 * 2 ^3=0+2+4+0=6 , 14%8 ist genau gleich 6 .
Verdammt, Computer sind einfach so vernünftig, dass man nichts dagegen tun kann
Mit anderen Worten, nur wenn die Array-Länge 2 hoch n beträgt, ist der Rest mit & erfolgreich. Aus diesem Grund muss die Hashmap beim Erweitern um das Zweifache erweitert werden.
Warum ist es so ein Zufall, dass die beiden genau gleich sind? Dies hängt mit dem Konzept einer Low-Bit-Maske zusammen. Wir werden hier nicht näher darauf eingehen.

Schauen wir uns im Anschluss an das Diagramm der vorherigen Frage diesen Schritt an, um zu verstehen:

 

Nachdem wir das Prinzip der Hash-Methode kennen und berechnen, müssen wir auch Folgendes wissen:

 Wo wird die Hash-Methode verwendet?

1. Die erste ist natürlich unsere klassischste Put-Methode. Der nachfolgende Putval berechnet den Index anhand des Hash-Werts.

2. Zweitens rufen wir, wenn wir die get-Methode verwenden, um ein Element abzurufen, die getNode-Methode auf, die den Hash-Wert verwendet.

Zusammenfassung

Die Hauptfunktion der Hash-Methode besteht darin, den HashCode-Wert des Schlüssels zu verarbeiten, um den endgültigen Hash-Wert zu erhalten. Da der HashCode-Wert des Schlüssels unsicher ist, kann es zu Hash-Konflikten kommen. Daher muss der Hash-Wert über einen bestimmten Algorithmus dem tatsächlichen Speicherort der HashMap zugeordnet werden.
Das Prinzip der Hash-Methode besteht darin, zuerst den HashCode-Wert des Schlüsselobjekts abzurufen und dann eine XOR-Operation für seine hohen und niedrigen Bits durchzuführen, um einen neuen Hash-Wert zu erhalten. Warum eine XOR-Operation durchführen? Da die hohen und niedrigen Bits von HashCode relativ gleichmäßig verteilt sind, besteht die Gefahr, dass Hash-Konflikte auftreten, wenn Sie sie einfach addieren oder Bitoperationen ausführen, und die XOR-Operation kann dieses Problem vermeiden. Der neue Hashwert wird dann modulo (mod) berechnet, um einen tatsächlichen Speicherort zu erhalten. Der Zweck dieser Modulo-Operation besteht darin, den Hash-Wert dem Index des Buckets (Bucket) zuzuordnen. Der Bucket ist ein Array in der HashMap. Jeder Bucket speichert eine verknüpfte Liste (oder einen rot-schwarzen Baum) und die geladenen Hash-Werte ​​sind gleich. Schlüssel-Wert-Paar (wenn es keinen gleichen Hash-Wert gibt, wird nur ein Schlüssel-Wert-Paar gespeichert).
Im Allgemeinen besteht die Hash-Methode von HashMap darin, den HashCode-Wert des Schlüsselobjekts zu verarbeiten, um den endgültigen Hash-Wert zu erhalten, und ihn über einen bestimmten Algorithmus dem tatsächlichen Speicherort zuzuordnen. Dieser Prozess bestimmt die Sucheffizienz von Schlüssel-Wert-Paaren innerhalb von HashMap.

おすすめ

転載: blog.csdn.net/weixin_52394141/article/details/131987773