Die Equals -Methode und die Hashcode -Methode in Java sind im Objekt, sodass jedes Objekt diese beiden Methoden hat. Manchmal müssen wir bestimmte Bedürfnisse umsetzen und diese beiden Methoden möglicherweise neu schreiben. Heute werden wir die Funktionen dieser beiden Methoden vorstellen.
Die Methoden Equals () und HashCode () werden verwendet, um in derselben Klasse zu vergleichen, insbesondere wenn das gleiche Klassenobjekt im Container wie eingestellt ist, um festzustellen, ob das platzierte Objekt dupliziert ist.
Hier müssen wir zuerst ein Problem verstehen:
Zwei Objekte mit Equals () Equals, HashCode () müssen gleich sein, und zwei Objekte mit Equals () können nicht nachweisen, dass ihr HashCode () nicht gleich ist. Mit anderen Worten, für zwei Objekte, deren Equals () -Methode nicht gleich ist, kann HashCode () gleich sein. (Mein Verständnis wird durch die Hash -Code -Konflikte verursacht, wenn er generiert wird)
Hier ist HashCode wie der Index jedes Zeichens im Wörterbuch und gleich () ist wie beim Vergleich verschiedener Wörter unter demselben Charakter im Wörterbuch. Genau wie im Wörterbuch sucht die Suche nach den beiden Wörtern "Selbst" und "spontan" unter dem Wort "Selbst" im Wörterbuch, wenn Equals () verwendet wird, um die Gleichheit der Wörter Abfrage zu bestimmen, ist es dasselbe Wort. Beispielsweise sind die beiden von Equals () verglichenen Wörter "Selbst", dann müssen die von der HashCode () -Methode erhaltenen Werte zu diesem Zeitpunkt gleich sein; Wenn Equals () -Methode die Wörter "Selbst" und "spontan" vergleicht, dann ist das Ergebnis, dass Sie nicht warten wollen, aber beide Wörter gehören zu den Wörtern "Selbst" und so ist bei der Suche nach Indizes, dh HashCode () das gleiche. Wenn Equals () die Wörter "Selbst" und "sie" vergleicht, sind auch die Ergebnisse unterschiedlich und die von HashCode () erzielten Ergebnisse sind zu diesem Zeitpunkt ebenfalls unterschiedlich.
Umgekehrt: HashCode () ist unterschiedlich und Equals () kann eingeführt werden; HashCode () ist gleich, gleich () kann gleich sein oder nicht gleich. In der Objektklasse ist die HashCode () -Methode eine lokale Methode, die den Adresswert des Objekts zurückgibt. Die Equals () -Methode in der Objektklasse vergleicht auch die Adresswerte der beiden Objekte. Wenn Equals () gleich ist, bedeutet dies, dass die Adresswerte der beiden Objekte ebenfalls gleich sind, natürlich Hashcode () gleich;
Gleichzeitig bietet der Hash -Algorithmus eine hohe Effizienz für die Suche nach Elementen
Wenn Sie herausfinden möchten, ob ein Objekt in einer Sammlung enthalten ist, wie schreiben Sie dann den ungefähren Programmcode?
Normalerweise nehmen Sie jedes Element eins nach dem anderen heraus, um sie mit dem von Ihnen gesuchten Objekt zu vergleichen. Wenn Sie feststellen, dass das Ergebnis des Equals -Methodenvergleichs zwischen einem Element und dem von Ihnen gesuchten Objekt aufhören und positive Informationen zurückgeben. Andernfalls geben Sie negative Informationen zurück. Wenn es in einer Sammlung viele Elemente gibt, z. B. 10.000 Elemente und nicht das von Ihnen gesuchte Objekt enthalten, bedeutet dies, dass Ihr Programm 10.000 Elemente aus der Sammlung herausnehmen und eins nach dem anderen vergleichen muss, um eine Schlussfolgerung zu erhalten.
Jemand hat einen Hash -Algorithmus erfunden, um die Effizienz des Findens von Elementen aus einem Satz zu verbessern. Auf diese Weise ist das Set in mehrere Lagerbereiche unterteilt. Jedes Objekt kann einen Hash -Code berechnen, und der Hash -Code kann gruppiert werden (berechnet unter Verwendung verschiedener Hash -Funktionen). Jede Gruppe entspricht einem bestimmten Speicherbereich. Gemäß dem Hash eines Objekts kann bestimmen, in welchem Bereich das Objekt gespeichert werden soll. Hashset verwendet einen Hash -Algorithmus, um auf die Sets von Objekten zuzugreifen. Es verwendet intern die Methode, um den Rest einer bestimmten Zahl N (diese Hash -Funktion ist die einfachste), um die Hash -Codes zu gruppieren und zu teilen. Die Objektklasse definiert eine HashCode () -Methode, um den Hash -Code jedes Java -Objekts zurückzugeben. Bei der Suche nach einem Objekt aus einer Hashset -Sammlung ruft das Java -System zunächst die HashCode () -Methode des Objekts auf, um die Hash -Code -Tabelle des Objekts zu erhalten. Finden Sie dann den entsprechenden Speicherbereich basierend auf dem Hashing und erhalten Sie schließlich jedes Element im Speicherbereich und vergleichen Sie ihn mit dem Objekt für die Equals -Methode. Auf diese Weise können Sie die Schlussfolgerung ziehen, ohne alle Elemente in der Sammlung zu durchqueren. Es ist zu erkennen, dass die Hashset -Sammlung eine gute Leistung von Objektabriefern hat, aber die Effizienz des Speichers von Objekten in der Hashset -Sammlung ist relativ niedrig, da Sie beim Hinzufügen eines Objekts zur Hashset -Sammlung zuerst den Hash -Code des Objekts berechnen und die Speicherort des Objekts in der Sammlung basierend auf diesem Hash -Code ermitteln. Um sicherzustellen, dass die Instanzobjekte einer Klasse normal in Hashset gespeichert werden können, müssen die Ergebnisse der beiden Instanzobjekte dieser Klasse ebenfalls gleich sein, wenn die Ergebnisse der Equals () -Methode verglichen sind. Das heißt, wenn das Ergebnis von obj1.equals (OBJ2) wahr ist, muss auch das Ergebnis des folgenden Ausdrucks wahr sein:
obj1.hashcode () == obj2.hashcode ()
Mit anderen Worten: Wenn wir die Equals -Methode eines Objekts neu schreiben, müssen wir seine Hashcode -Methode neu schreiben. Wenn wir jedoch seine HashCode -Methode nicht neu schreiben, gibt die HashCode -Methode im Objektobjekt immer die Hash -Adresse eines Objekts zurück, und diese Adresse ist niemals gleich. Selbst wenn die Equals -Methode zu diesem Zeitpunkt neu geschrieben wird, wird es keinen spezifischen Effekt geben, denn wenn die HashCode -Methode nicht warten möchte, wird die Equals -Vergleichsmethode nicht aufgerufen, sodass sie bedeutungslos ist.
Wenn die HashCode () -Methode einer Klasse nicht den oben genannten Anforderungen entspricht, sollten die Ergebnisse des Vergleichs zwischen den beiden Instanzobjekten dieser Klasse gleichzeitig mit der Equals () -Methode gleichzeitig gespeichert werden. Wenn sie jedoch im Hashset -Satz gespeichert werden, da der Rückgabewert ihrer HashCode () -Methode unterschiedlich ist (der Rückgabewert der HashCode -Methode im Objekt ist immer anders), kann das zweite Objekt in einen anderen Bereich eingebaut werden, als das erste Objekt zuerst gemäß der Hash -Code -Berechnung in einen anderen Bereich mit dem ersten Objekt vergleichen kann. Es kann möglicherweise die Equals -Methode vergleichen. Es kann möglicherweise in der Hadern -Kollektor -Kollektion mit dem Hadern mit der Haderset -Kollektion gespielt werden. Die HashCode () -Methode in der Objektklasse kann die Anforderungen des Objekts, das im HashSet gespeichert wird, nicht erfüllen, da der Rückgabewert aus der Speicheradresse des Objekts berechnet wird. Der von demselbe Objekt zurückgegebene Hash -Wert während des Programmlaufs ist immer unverändert. Solange die Vergleichsergebnisse ihrer Equals -Methode gleich sind, ist der Rückgabewert ihrer Standard -HashCode -Methode unterschiedlich, solange es zwei verschiedene Instanzobjekte sind, auch wenn die Vergleichsergebnisse ihrer Equals -Methode gleich sind.
Schauen wir uns ein bestimmtes Beispiel an:
RectObject -Objekt: Paket com.weijia.demo; public class rectObject {public int x; öffentlich int y; public rectObject (int x, int y) {this.x = x; this.y = y; } @Override public int HashCode () {final int prime = 31; int result = 1; Ergebnis = Prime * Ergebnis + x; Ergebnis = Prime * Ergebnis + y; Rückgabeergebnis; } @Override public boolean Equals (Objekt obj) {if (this == obj) return true; if (obj == null) return false; if (getClass ()! = obj.getClass ()) return false; endgültiges rectObject Andere = (rectObject) obj; if (x! = other.x) {return false; } if (y! = other.y) {return false; } Return true; }} Wir haben den HashCode- und Equals -Methoden im übergeordneten Klassenobjekt überschrieben und feststellen, dass in den HashCode- und Equals -Methoden, wenn die X- und Y -Werte der beiden RectObject -Objekte gleich sind, ihre HashCode -Werte gleich sind und Equals RECHT RETTS GESAUT.
Hier ist der Testcode:
Paket com.weijia.demo; import Java.util.hashset; öffentliche Klasse Demo {public static void main (String [] args) {Hashset <RectObject> set = new Hashset <RectObject> (); RectObject r1 = neues rectObject (3,3); RectObject r2 = neues rectObject (5,5); RectObject r3 = neues rectObject (3,3); set.add (r1); set.add (r2); set.add (r3); set.add (r1); System.out.println ("Größe:"+set.size ()); }} Wir haben vier Objekte in das Hashset gespeichert und die Größe der Set -Sammlung gedruckt. Was ist das Ergebnis?
Auslaufergebnis: Größe: 2
Warum ist es 2? Dies ist sehr einfach, da wir die HashCode -Methode der RectObject -Klasse neu schreiben. Solange die X- und Y -Attributwerte des RectObject -Objekts gleich sind, sind auch seine Hashcode -Werte gleich. Vergleichen Sie also zunächst die Hashcode -Werte. Die x- und y -Attributwerte von R1- und R2 -Objekten sind nicht gleich, sodass ihre Hashcodes unterschiedlich sind, sodass das R2 -Objekt eingefügt werden kann, aber die X- und Y -Attributwerte des R3 -Objekts sind die gleichen wie die Attributwerte des R1 -Objekts, sodass der Hashcode gleich ist. Zu diesem Zeitpunkt vergleichen wir die Equals -Methode von R1 und R3, da die x- und y -Werte der beiden gleich sind, sodass R1- und R3 -Objekte gleich sind, sodass R3 nicht eingefügt werden kann. Außerdem wird das Hinzufügen eines R1 am Ende nicht hinzugefügt, sodass nur ein zwei Objekte, R1 und R2 im Satz eingestellt sind.
Als nächstes kommentieren wir die HashCode -Methode im RectObject -Objekt, dh die HashCode -Methode im Objektobjekt nicht überschreiben und den Code ausführen:
Auslaufergebnis: Größe: 3
Dieses Ergebnis ist auch sehr einfach. Beurteilen Sie zunächst den Hashcode von R1 -Objekt und R2 -Objekt. Da die HashCode -Methode im Objekt das Konvertierungsergebnis der lokalen Speicheradresse des Objekts zurückgibt, ist der HashCode verschiedener Instanzobjekte unterschiedlich. In ähnlicher Weise, da der Hashcode von R3 und R1 ebenfalls ungleich ist, aber R1 == R1, gibt es im letzten Satz nur drei Objekte R1, R2 und R3, so dass die Größe 3 beträgt 3
Als nächstes kommentieren wir den Inhalt der Equals -Methode im RectObject -Objekt, kehren direkt False zurück, ohne die HashCode -Methode zu kommentieren, und führen den Code aus:
Auslaufergebnis: Größe: 3
Dieses Ergebnis ist etwas unerwartet. Lassen Sie es uns analysieren:
Zuerst vergleichen die Objekte von R1 und R2 HashCode, die nicht gleich sind, so dass R2 in eingestellt ist, und dann die Hashcode -Methoden von R3 und vergleichen R1 und R3, die gleich sind, und vergleichen dann ihre Equals -Methoden. Da die Equals -Methode immer False zurückgibt, sind R1 und R3 ebenfalls ungleich und es ist nicht erforderlich, R3 und R2 zu erwähnen. Die Hashcodes ihrer beiden sind nicht gleich, daher werden R3 in Set eingebaut und dann R4 ansehen und R1 und R4 -Fund vergleichen, dass Hashcodes gleich sind. Beim Vergleich der Equals -Methode, da Equals false zurückgibt, sind R1 und R4 nicht gleich, das gleiche R2 und R4 sind ebenfalls ungleich und R3 und R4 sind ebenfalls ungleich, sodass R4 in das Satz gesetzt werden kann, sodass das Ergebnis eine Größe sein sollte: 4, also warum ist es 3?
Zu diesem Zeitpunkt müssen wir den Quellcode von Hashset überprüfen. Hier ist der Quellcode der Methode hinzufügen in Hashset:
/*** fügt diesem Satz das angegebene Element hinzu, wenn es noch nicht vorhanden ist. * Formaler fügt das angegebene Element <Tt> E </tt> zu diesem Satz hinzu, wenn * dieser Satz kein Element <Tt> e2 </tt> enthält, so dass * <tt> (e == null? E2 == null: e.equals (e2)) </tt>. * Wenn dieser Satz bereits das Element enthält, verlässt der Aufruf den Satz * unverändert und gibt <tt> false </tt> zurück. * * @param e Element, das zu diesem Satz hinzugefügt werden soll }
Hier können wir sehen, dass Hashset tatsächlich basierend auf HashMap implementiert wird. Wir klicken auf die Put -Methode von HashMap, der Quellcode lautet wie folgt:
/*** assoziiert den angegebenen Wert mit dem angegebenen Schlüssel in dieser Karte. * Wenn die Karte zuvor eine Zuordnung für den Schlüssel enthielt, wird der alte * Wert ersetzt. * * @param -Schlüssel, mit dem der angegebene Wert zugeordnet sein soll * @param -Wert, der dem angegebenen Schlüssel zugeordnet ist * (A <tt> null </tt> kann auch angeben, dass die Karte * zuvor zugeordnetes <tt> null </tt> mit <Tt> Schlüssel </tt>.) */Public v put (k key, v -Wert) {if (key == null) return putfornullekey (value); int Hash = Hash (Schlüssel); 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; } Schauen wir uns hauptsächlich die Urteilsbedingungen von if an.
Zunächst bestimmen wir, ob der HashCode gleich ist. Wenn es nicht gleich ist, überspringen Sie es direkt. Wenn es gleich ist, vergleichen Sie, ob diese beiden Objekte gleich oder die Equals -Methode dieser beiden Objekte sind. Da es durchgeführt oder betrieben wird, können wir es hier erklären, solange man wahr ist. Tatsächlich beträgt die Größe des obigen Satzes 3, da der letzte R1 nicht eingefügt wurde, und es wurde angenommen, dass R1 == R1 true zurückgegeben wurde, so dass er nicht eingefügt wurde. Die Größe des Satzes beträgt 3. Wenn wir die HashCode -Methode so einstellen, dass sie immer falsch zurückgegeben werden, beträgt dieser Satz 4.
Schauen wir uns schließlich das durch Hashcode verursachte Speicherleck an: Sehen Sie sich den Code an:
Paket com.weijia.demo; import Java.util.hashset; öffentliche Klasse Demo {public static void main (String [] args) {Hashset <RectObject> set = new Hashset <RectObject> (); RectObject r1 = neues rectObject (3,3); RectObject r2 = neues rectObject (5,5); RectObject r3 = neues rectObject (3,3); set.add (r1); set.add (r2); set.add (r3); r3.y = 7; System.out.println ("Größe vor Löschen:"+set.size ()); set.remove (r3); System.out.println ("Größe nach Löschen:"+set.size ()); }} Auslaufergebnisse:
Größe vor Löschen: 3
Löschte Größe: 3
Rush, ich fand ein Problem und es war ein großes Problem. Wir haben Remove angerufen, um das R3 -Objekt zu löschen und zu löschen, dass es gelöscht worden war, aber tatsächlich nicht gelöscht wurde. Dies wird als Speicherleckage bezeichnet, bei dem es sich um ein nicht verwendetes Objekt handelt, aber es befindet sich immer noch im Speicher. Nachdem wir so oft gearbeitet haben, explodiert der Speicher. Schauen Sie sich den Quellcode von entfernen an:
/*** entfernt das angegebene Element aus diesem Satz, wenn es vorhanden ist. * Formell wird ein Element <tt> e </tt> so entfernt, dass * <tt> (o == null? E == null: o.equals (e)) </tt>, * Wenn dieser Satz ein solches Element enthält. Gibt <tt> true </tt> zurück, wenn * Dieser Satz enthielt das Element (oder äquivalent, wenn sich dieses Satz * infolge des Aufrufs geändert hat). (Dieser Satz enthält das * Element nicht, sobald der Anruf zurückgegeben wird.) * * @Param o Objekt, das aus diesem Satz entfernt werden soll, falls vorhanden * @return <tt> true </ tt> ist, wenn das Satz das angegebene Element enthielt */ public boolean entfernen (Objekt o) {return map.ReMove (o) == vorhanden; } Schauen Sie sich dann den Quellcode der Methode entfernen:
/*** entfernt die Zuordnung für den angegebenen Schlüssel aus dieser Karte, falls vorhanden. * * @Param -Schlüssel, deren Zuordnung aus der Karte entfernt werden soll * (A <tt> null </tt> Rückgabe kann auch angeben, dass die Karte * zuvor zugeordnetes <tt> null </tt> mit <Tt> Schlüssel </tt>.) */Public v remove (Objektschlüssel) {Eintrag <k, v> e = remedEnryForkey (Schlüssel); return (e == null? null: e.value); } Schauen wir uns den Quellcode von RemeentryForkey -Methoden an:
/** * entfernt und gibt den Eintrag zurück, der dem angegebenen Schlüssel * in der HashMap zugeordnet ist. Gibt NULL zurück, wenn der HashMap für diesen Schlüssel keine Zuordnung * enthält. */ Finale Eintrag <k, v> remedEentryForkey (Objektschlüssel) {int Hash = (key == null)? 0: Hash (Schlüssel); int i = indexFor (Hash, Tabelle.length); Eintrag <k, v> prep = table [i]; Eintrag <k, v> e = prev; while (e! = null) {Eintrag <k, v> next = e.Next; Objekt k; if (e.hash == Hash && ((k = E.Key) == Key || (Key! = null && key.equals (k)))) {modcount ++; Größe--; if (prev == e) Tabelle [i] = Weiter; sonst prev.Next = Weiter; E.RecordReMoval (dies); Rückkehr e; } prew = e; E = Weiter; } return e; } Wir sehen, dass wir beim Aufrufen der Methode entfernen zuerst den HashCode -Wert des Objekts verwenden, um das Objekt zu finden und es dann zu löschen. Dieses Problem liegt daran, dass wir den Wert des y -Attributs des R3 -Objekts geändert haben. Und da die HashCode -Methode des RectObject -Objekts einen y -Wert hat, der am Vorgang beteiligt ist, hat sich der HashCode des R3 -Objekts geändert, sodass R3 in der Methode entfernen nicht zu finden ist, sodass die Deletion fehlgeschlagen ist. Das heißt, der Hashcode von R3 hat sich geändert, aber der Ort, an dem er speichert, wurde nicht aktualisiert und befindet sich noch an seinem ursprünglichen Ort. Wenn wir also seinen neuen Hashcode verwenden, um ihn zu finden, werden wir ihn definitiv nicht finden.
Tatsächlich ist die obige Methode sehr einfach zu implementieren: Wie in der Abbildung gezeigt:
Eine sehr einfache lineare Hash -Tabelle, die verwendete Hash -Funktion ist Mod, der Quellcode lautet wie folgt:
/*** Gibt den Index für Hash -Code h zurück. */ static int indexfor (int h, int länge) {return H & (Länge-1); } Dies ist eigentlich ein Mod -Betrieb, aber diese Art des Betriebs ist effizienter als % Betrieb.
1,2,3,4,5 bedeutet das Ergebnis von MOD, und jedes Element entspricht einer verknüpften Listenstruktur. Wenn Sie also einen Eintrag <k, v> löschen möchten, erhalten Sie zuerst HashCode, um den Headerknoten der verlinkten Liste zu erhalten, und dann die verlinkte Liste durchzusetzen. Wenn HashCode und Gleichen gleich sind, löschen Sie dieses Element.
Das obige Speicherleck sagt mir eine Nachricht: Wenn wir am HashCode -Betrieb des Attributwerts des Objekts teilnehmen, können wir den Attributwert beim Löschen nicht ändern, sonst treten ernsthafte Probleme auf.
Tatsächlich können wir auch die HashCode -Methode und die gleichzeitige Methode der Objekttypen untersuchen, die den 8 grundlegenden Datentypen entsprechen.
Unter ihnen ist der HashCode des Basistyps in 8 sehr einfach, ihre numerische Größe direkt zurückzugeben. Das String -Objekt erfolgt über eine komplexe Berechnungsmethode, diese Berechnungsmethode kann jedoch sicherstellen, dass, wenn die Werte dieser Zeichenfolge gleich sind, ihr HashCode gleich ist. Die 8 grundlegenden Arten von Equals -Methoden sind, um numerische Werte direkt zu vergleichen, und der String -Typ Equals -Methode vergleicht die Werte von Zeichenfolgen.
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.