Ich glaube, die meisten Menschen sind mit der Hash -Tabellendatenstruktur vertraut, und an vielen Stellen werden Hash -Tabellen verwendet, um die Suchffizienz zu verbessern. In Javas Objektklasse gibt es eine Methode:
public native int hashcode ();
Nach der Erklärung dieser Methode ist ersichtlich, dass die Methode einen numerischen Wert des Typs in INT zurückgibt und eine lokale Methode ist, sodass in der Objektklasse keine spezifische Implementierung angegeben ist.
Warum braucht die Objektklasse eine solche Methode? Was ist ihre Funktion? Heute werden wir die Hashcode -Methode im Detail diskutieren.
1. Die Funktion der Hashcode -Methode
Für Programmiersprachen, die Containertypen enthalten, ist HashCode im Grunde genommen beteiligt. Gleiches gilt für Java. Die Hauptfunktion der HashCode-Methode besteht darin, normal mit Hash-basierten Sets zu arbeiten, wie Hash-Sets gehören Hashset, HashMap und Hashtable.
Warum sagst du das? Betrachten Sie eine Situation, in der ein Objekt in einer Sammlung festgelegt wird, ob das Objekt bereits in der Sammlung vorhanden ist? (Hinweis: Duplikate Elemente sind in der Sammlung nicht zulässig)
Vielleicht denken die meisten Menschen daran, die Equals -Methode zu bezeichnen, um einzeln zu vergleichen, und diese Methode ist in der Tat machbar. Wenn es jedoch bereits zehntausend Daten oder mehr Daten im Satz gibt, ist die Effizienz zwangsläufig ein Problem, wenn die Equals -Methode zum Vergleich von einzelnen nacheinander verwendet wird. Zu diesem Zeitpunkt wird die Funktion der HashCode -Methode reflektiert. Wenn der Sammlung ein neues Objekt hinzugefügt werden soll, wird die HashCode -Methode des Objekts zuerst aufgerufen, um den entsprechenden HashCode -Wert zu erhalten. Tatsächlich wird bei der spezifischen Implementierung von HashMap eine Tabelle verwendet, um den HashCode -Wert des gespeicherten Objekts zu speichern. Wenn in der Tabelle keinen HashCode -Wert gibt, kann er ohne Vergleich direkt gespeichert werden. Wenn der HashCode -Wert vorhanden ist, wird seine Equals -Methode aufgefordert, mit dem neuen Element zu vergleichen. Wenn dies gilt, werden andere Adressen nicht gespeichert. Daher gibt es hier ein Problem der Konfliktlösung. Auf diese Weise wird die Häufigkeit, mit der die Equals -Methode tatsächlich aufgerufen wird, stark reduziert. Einfach gesagt: Die HashCode -Methode in Java ordnet die Informationen zum Objekt (z. B. die Speicheradresse des Objekts, des Feldes des Objekts usw.) in einen numerischen Wert gemäß bestimmten Regeln ab, und dieser Wert wird als Hash -Wert bezeichnet. Der folgende Code ist die spezifische Implementierung der Put -Methode in java.util.hashmap:
public v put (k key, v value) {if (key == null) return putFornullKey (Wert); int hash = hash (key.hashcode ()); int i = indexFor (Hash, Tabelle.length); für (Eintrag <k, v> e = table [i]; e! = null; e = e.next) {Objekt k; if (e.hash == Hash && ((k = E.Key) == Key || key.equals (k))) {v oldValue = e.Value; E. value = Wert; E. recordaccess (this); kehren Sie OldValue zurück; }} modcount ++; AddEntry (Hash, Schlüssel, Wert, i); null zurückkehren; } Die Put -Methode wird verwendet, um dem HashMap ein neues Element hinzuzufügen. Aus der spezifischen Implementierung der Put -Methode können wir wissen, dass die HashCode -Methode zuerst aufgerufen wird, um den HashCode -Wert des Elements zu erhalten, und dann zu überprüfen, ob der HashCode -Wert in der Tabelle vorhanden ist. Wenn es vorhanden ist, rufen Sie die Equals -Methode an, um festzustellen, ob das Element existiert. Wenn es vorhanden ist, aktualisieren Sie den Wertwert und fügen Sie das neue Element dem HashMap hinzu. Von hier aus können wir sehen, dass die HashCode -Methode vorhanden ist, um die Anzahl der Aufrufe der Equals -Methode zu verringern und somit die Programmeffizienz zu verbessern.
Einige Freunde denken fälschlicherweise, dass HashCode standardmäßig die Speicheradresse des Objekts zurückgibt. Tatsächlich ist diese Ansicht unvollständig. Es ist wahr, dass einige JVMs bei der Implementierung die Speicheradresse des Objekts direkt zurückgeben. Dies ist jedoch die meiste Zeit nicht der Fall. Es kann nur gesagt werden, dass die Speicheradresse möglicherweise damit zusammenhängt. Das Folgende ist die Implementierung der Generierung von Hash -Hash -Werten in Hotspot JVM:
static inline intptr_t get_next_hash (thread * self, oop obj) {intptr_t value = 0; if (HashCode == 0) {// Dieses Formular verwendet einen unbewachten globalen Park-Miller-RNG, // Es ist also möglich, dass zwei Threads das gleiche RNG rennen und generieren. // Auf dem MP -System haben wir viel RW -Zugriff auf einen globalen Zugriff, sodass der // Mechanismus viel Kohärenzverkehr induziert. value = os :: random (); } else if (HashCode == 1) {// Diese Variation hat die Eigenschaft, stabil (idempotent) // zwischen STW -Operationen zu sein. Dies kann in einigen der 1-0 // Synchronisationsschemata nützlich sein. intptr_t adDrbits = intptr_t (obj) >> 3; value = addrbits ^ (addrbits >> 5) ^ gvars.stwrandom; } else if (HashCode == 2) {value = 1; // für Sensitivitätstests} else if (HashCode == 3) {value = ++ gvars.hcsequence; } else if (HashCode == 4) {value = intptr_t (obj); } else {// Marsaglias XOR-SHIFT-Schema mit einem threadspezifischen Zustand // Dies ist wahrscheinlich die beste Gesamtimplementierung-wir werden dies wahrscheinlich zum Standard in zukünftigen Veröffentlichungen machen. unsigned t = self-> _ HashStatex; t ^= (t << 11); Self-> _ HashStatex = self-> _ Hashstatey; Self-> _ hashstatey = self-> _ Hashstatez; Self-> _ HashStatez = self-> _ Hashstatew; unsigned v = self-> _ HashStatew; v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)); Self-> _ hashstatew = v; Wert = v; } value & = markoopDesc :: Hash_mask; if (value == 0) value = 0xbad; Assert (Wert! = markoopdesc :: no_hash, "invariante"); Tevent (HashCode: generieren); Rückgabewert;}Diese Implementierung befindet sich in der Datei Hotspot/SRC/Share/VM/Runtime/Synchronizer.cpp.
Einige Leute können daher sagen, dass wir direkt beurteilen können, ob zwei Objekte auf der Grundlage des HashCode -Werts gleich sind? Es ist sicherlich nicht möglich, da verschiedene Objekte den gleichen HashCode -Wert erzeugen können. Obwohl es nicht möglich ist zu beurteilen, ob zwei Objekte auf der Grundlage des HashCode -Werts gleich sind, können Sie direkt beurteilen, dass die beiden Objekte nicht gleich basierend auf dem HashCode -Wert sind. Wenn die HashCode -Werte der beiden Objekte nicht gleich sind, müssen sie zwei verschiedene Objekte sein. Wenn Sie feststellen möchten, ob zwei Objekte wirklich gleich sind, müssen Sie die Equals -Methode verwenden.
Das heißt, für zwei Objekte müssen die HashCode -Werte der beiden Objekte gleich sein, wenn das Ergebnis, das durch Aufrufen der Equals -Methode erhalten wurde, gleich sein.
Wenn das von der Equals -Methode erhaltene Ergebnis falsch ist, sind die Hashcode -Werte der beiden Objekte möglicherweise nicht unterschiedlich.
Wenn die HashCode -Werte der beiden Objekte nicht gleich sind, muss das von der Equals -Methode erhaltene Ergebnis falsch sein.
Wenn die HashCode -Werte der beiden Objekte gleich sind, ist das von der Equals -Methode erhaltene Ergebnis unbekannt.
2. Methode für Ausgleiche und Hashcode -Methode
In einigen Fällen müssen Programmierer beim Entwerfen einer Klasse die Equals -Methode wie die String -Klasse neu schreiben. Achten Sie jedoch darauf, dass sie während der Umschreiben der Equals -Methode die HashCode -Methode neu schreiben müssen. Warum sagst du das?
Lassen Sie uns unten ein Beispiel sehen:
Paket com.cxh.test1; import java.util.hashMap; import Java.util.hashset; import Java.util.set; Klasse people {private String name; privates int Alter; public people (string name, int ay) {this.name = name; this.age = Alter; } public void setage (int age) {this.age = älter; } @Override public boolean Equals (Objekt obj) {// Todo automatisch generierter Methode Stub return this.name.equals ((() obj) .Name) && this.age == ((Menschen) obj) .age; }} public class main {public static void main (String [] args) {people p1 = new People ("Jack", 12); System.out.println (p1.hashcode ()); HashMap <People, Integer> HashMap = New HashMap <People, Integer> (); Hashmap.put (p1, 1); System.out.println (Hashmap.get (neue Leute ("Jack", 12));}}Hier habe ich die Equals -Methode nur neu schreiben, was bedeutet, dass wenn zwei Personen Objekte denselben Namen und Alter haben, sie als dieselbe Person angesehen werden.
Die ursprüngliche Absicht dieses Codes besteht darin, das Ergebnis von "1" auszugeben, aber tatsächlich gibt es "null" aus. Warum? Der Grund dafür ist, dass Sie vergessen, die HashCode -Methode neu zu schreiben und gleichzeitig die Equals -Methode neu zu schreiben.
Obwohl zwei Objekte mit demselben Namen und demselben Alter logischerweise als gleich bestimmt sind (ähnlich der String -Klasse), sollte bekannt sein, dass die HashCode -Methode standardmäßig die Speicheradresse des Objekts abbildert. Dann ist es nicht überraschend, dass das Ausgabeergebnis des obigen Code "null" ist. Der Grund ist sehr einfach, das Objekt zeigt durch P1 und
System.out.println (HashMap.get (neue Leute ("Jack", 12); Die neuen Leute ("Jack", 12) in diesem Satz generiert zwei Objekte, und ihre Speicheradressen müssen unterschiedlich sein. Das Folgende ist die spezifische Implementierung von HashMap -Methode: Get -Methode:
public v get (Objektschlüssel) {if (key == null) return getFornullKey (); int hash = hash (key.hashcode ()); für (Eintrag <k, v> e = table [indexFor (Hash, Tabelle.Length)]; e! = null; e = E.Next) {Objekt k; if (e.hash == Hash && ((k = E.Key) == Key || key.equals (k))) return e.Value; } return null; }Wenn der HashMap verwendet wird, um zu erhalten, weil die erhaltenen Hashcdoe -Werte unterschiedlich sind (beachten Sie, dass der obige Code in einigen Fällen den gleichen HashCode -Wert erhalten kann, aber die Wahrscheinlichkeit ist relativ gering, da die Speicheradressen der beiden Objekte unterschiedlich sind, ist es möglich, dasselbe Hashcode -Wert zu erhalten, sodass der für die Schleife nicht in der Methode ausgeführt wird.
Wenn Sie möchten, dass der obige Code das Ergebnis "1" ausgibt, ist es sehr einfach. Sie müssen nur die HashCode -Methode neu schreiben und die Equals -Methode und die Hashcode -Methode immer logisch konsistent erstellen.
Paket com.cxh.test1; import java.util.hashMap; import Java.util.hashset; import Java.util.set; Klasse people {private Zeichenfolge Name; privates int Alter; public people (string name, int ay) {this.name = name; this.age = Alter; } public void setage (int age) {this.age = älter; } @Override public int HashCode () {// Todo automatisch generierte Methode Stub return name.hashcode ()*37+älter; } @Override public boolean Equals (Objekt obj) {// Todo automatisch generierter Methode Stub return this.name.equals ((() obj) .Name) && this.age == ((Menschen) obj) .age; }} public class main {public static void main (String [] args) {people p1 = new People ("Jack", 12); System.out.println (p1.hashcode ()); HashMap <People, Integer> HashMap = New HashMap <People, Integer> (); Hashmap.put (p1, 1); System.out.println (Hashmap.get (neue Leute ("Jack", 12)); }}Auf diese Weise wird das Ausgabeergebnis "1" sein.
Die folgende Passage wird aus dem Buch effektiv Java ausgerichtet:
Während der Programmausführung muss die HashCode -Methode, solange die im Vergleichsbetrieb der Equals -Methode verwendeten Informationen nicht geändert wurden, die gleiche Ganzzahl konsequent zurückgeben.
Wenn die beiden Objekte gemäß der Equals -Methode gleich sind, muss das Aufrufen der HashCode -Methode der beiden Objekte das gleiche Integer -Ergebnis zurückgeben.
Wenn die beiden Objekte gemäß der Equals -Methode verglichen werden, gibt die HashCode -Methode nicht unbedingt verschiedene ganze Ganzzahlen zurück.
Es ist leicht, den zweiten und dritten Artikel zu verstehen, aber der erste Artikel wird oft ignoriert. Es gibt auch eine Passage ähnlich wie die erste in Seite P495 im Buch "Java Programming Thought":
"Der wichtigste Faktor beim Entwerfen von HashCode () ist: Wenn Sie Hashcode () auf demselben Objekt aufrufen, sollte der gleiche Wert generiert werden. Wenn ein Objekt mit Put () ein Objekt hinzugefügt wird, und ein anderer Hashcode -Wert generiert wird, wenn sie mit GET () ausgezeichnet werden (), dann können die Objekte nicht erhalten werden. HashCode () -Methode generiert einen anderen Hash -Code. "
Hier ist ein Beispiel:
Paket com.cxh.test1; Import Java.util.hashMap; Import Java.util.hashset; Import Java.util.set; Klasse People {private String name; privates int Alter; public people (string name, int ay) {this.name = name; this.age = Alter; } public void setage (int age) {this.age = älter; } @Override public int HashCode () {// Todo automatisch generierte Methode Stub return name.hashcode ()*37+älter; } @Override public boolean Equals (Objekt obj) {// Todo automatisch generierter Methode Stub return this.name.equals ((() obj) .Name) && this.age == ((Menschen) obj) .age; }} public class main {public static void main (String [] args) {people p1 = new People ("Jack", 12); System.out.println (p1.hashcode ()); HashMap <People, Integer> HashMap = New HashMap <People, Integer> (); Hashmap.put (p1, 1); P1.Setage (13); System.out.println (HashMap.get (p1)); }}Das Ergebnis dieser Codeausgabe ist "null", und jeder muss sich über die Gründe klar machen.
Bei der Gestaltung von HashCode -Methoden und -Methoden, wenn die Daten im Objekt volatil sind, ist es daher am besten, in den Equals -Methoden und HashCode -Methoden nicht auf dieses Feld zu stützen.
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.