Die Methoden von HashCode () und Equals () können als wichtiges Merkmal von Javas vollständig objektorientiertem Merkmal bezeichnet werden. Es erleichtert unsere Programmierung und bringt auch viele Gefahren mit sich. In diesem Artikel werden wir diskutieren, wie diese beiden Methoden korrekt verstanden und angewendet werden können.
Wenn Sie sich entscheiden, die Equals () -Methode neu zu schreiben, müssen Sie sich über die damit eingeführten Risiken klar machen und sicherstellen, dass Sie eine robuste Equals () -Methode schreiben können. Eine Sache, die Sie beachten müssen, ist, dass Sie nach dem Umschreiben von Equals () die Methode von HashCode () neu schreiben müssen. Die spezifischen Gründe werden später erklärt.
Schauen wir uns zunächst die Beschreibung der Equals () -Methode in der Javase 7 -Spezifikation an:
・ Es ist reflexiv: Für jeden Nicht-Null-Referenzwert x, x.equals(x) true zurückgeben.
・ Es ist symmetrisch: Für alle Nicht-Null-Referenzwerte x und y, x.equals(y) nur dann true zurückgeben, wenn y.equals(x) true zurückgibt.
・ Es ist transitiv: Für alle Nicht-Null-Referenzwerte x, y, und z, wenn x.equals(y) true zurückgibt und y.equals(z) true zurückgibt, dann sollte x.equals(z) true.
・ Es ist konsistent: Für alle Nicht-Null-Referenzwerte x und y , mehrere Aufrufe von x.equals(y) geben konsequent true oder konsequent false zurück, vorausgesetzt, keine Informationen, die in gleiche Vergleiche auf den Objekten verwendet werden, werden geändert.
・ Für jeden Nicht-Null-Referenzwert x, x.equals(null) false zurückgeben.
Diese Passage verwendet viel Numerologie in diskreten Mathematik. Lassen Sie mich eine kurze Erklärung geben:
1. Reflexivität: A.equals (a) muss wahr zurückkehren.
2. Symmetrie: Wenn A.equals (b) wahr zurückgibt, muss B.Equals (a) auch wahr zurückkehren.
3. Übertragung: Wenn A.equals (b) wahr ist und B.Equals (c) wahr ist, muss A.equals (c) ebenfalls wahr sein. Um es unverblümt auszudrücken, a = b, b = c, dann a = C.
4. Konsistenz: Solange sich der Zustand der A- und B -Objekte nicht ändert, muss A.equals (b) immer wahr zurückkehren.
5. A.Equals (Null), um falsch zurückzukehren.
Ich glaube, solange Menschen, die in der Mathematik nicht professionell sind, die oben genannten Dinge nicht nennen werden. In der tatsächlichen Anwendung müssen wir nur die Equals () -Methode gemäß bestimmten Schritten neu schreiben. Um die Bequemlichkeit der Erklärung zu ermöglichen, definieren wir zunächst eine Programmiererklasse (Coder):
Klassencodierer {privater Zeichenfolge Name; privates int Alter; // Getters und Setter}Was wir wollen, ist, dass, wenn die Namen und Alter der beiden Programmierobjekte gleich sind, wir glauben, dass diese beiden Programmierer gleich sind. Zu diesem Zeitpunkt müssen wir seine Equals () -Methode umschreiben. Da die Standardeinstellung gleich () tatsächlich bestimmt, ob zwei Referenzen auf das gleiche Objekt intrinsisch verweisen, entspricht er ==. Befolgen Sie beim Umschreiben die folgenden drei Schritte:
1. Bestimmen Sie, ob es Ihnen gleich ist.
if (other == this) return true;
2. Verwenden Sie den Instanzoperator, um festzustellen, ob andere ein Objekt des Typs Codierer ist.
if (! (Andere Instanz des Codierers)) return false;
3. Vergleichen Sie die Datendomänen, den Namen und die Altersdomänen, die Sie in der Coder -Klasse anpassen, und Sie dürfen keine verpassen.
Codierer o = (Coder) Andere; return o.name.equals (name) && o.age == Alter;
Wenn man das sieht, kann jemand in Schritt 3 eine Besetzung gibt. Wenn jemand ein Objekt der Ganzzahlklasse in dieses Gleiche übergibt, wird er dann eine ClassCastException werfen? Diese Sorge ist tatsächlich überflüssig. Da wir im zweiten Schritt das Urteil über die Instanz vorgenommen haben, wenn andere ein Nicht-Koder-Objekt oder sogar ein anderes Null ist, wird Falsch in diesem Schritt direkt zurückgegeben, so dass der nachfolgende Code nicht die Möglichkeit bietet, ausgeführt zu werden.
Die oben genannten drei Schritte sind auch die in <effektiven Java> empfohlenen Schritte, die im Grunde genommen sicherstellen können, dass es keinen Fehler gibt.
In Javase 7 Spezifikation,
"Beachten Sie, dass es im Allgemeinen notwendig ist, die HashCode -Methode zu überschreiben, wenn diese Methode (gleich) überschrieben ist, um den allgemeinen Vertrag für die HashCode -Methode zu erhalten, in der festgelegt wird, dass gleiche Objekte gleiche Hash -Codes haben müssen."
Wenn Sie die Methode Equals () neu schreiben, denken Sie daran, die HashCode () -Methode neu zu schreiben. Wir haben Hash -Tabellen in den Universitätscomputerdatenstrukturkursen gelernt. Die HashCode () -Methode dient der Hash -Tabelle.
Wenn wir eine Sammelklasse verwenden, die mit Hash wie Hash, wie HashMap und Hashset, beginnt, wird HashCode () implizit aufgerufen, um eine Hash -Mapping -Beziehung zu erstellen. Wir werden dies später erklären. Hier konzentrieren wir uns zuerst auf das Schreiben der HashCode () -Methode.
<Effektiver Java> bietet eine Schreibmethode, die Hash -Konflikte im größten Teil vermeiden kann, aber ich persönlich denke, dass es nicht notwendig ist, für allgemeine Anwendungen so viel Ärger zu machen. Wenn Sie Zehntausende oder Millionen von Objekten in Ihrer Anwendung speichern müssen, sollten Sie die im Buch angegebenen Methoden ausschließlich befolgen. Wenn Sie eine kleine und mittelgroße Anwendung schreiben, reichen die folgenden Prinzipien aus:
Es ist notwendig, sicherzustellen, dass alle Mitglieder des Codiererobjekts im HashCode reflektiert werden können.
In diesem Beispiel können wir Folgendes schreiben:
@Override public int HashCode () {int result = 17; Ergebnis = Ergebnis * 31 + name.hashcode (); Ergebnis = Ergebnis * 31 + Alter; Rückgabeergebnis; }Wo int result = 17 Sie es auch auf 20, 50 usw. ändern können, war ich plötzlich neugierig und wollte sehen, wie die HashCode () -Methode in der String -Klasse implementiert ist. Überprüfen Sie die Dokumentation und wissen Sie:
"Gibt einen Hash -Code für diesen Zeichenfolge zurück. Der Hash -Code für ein String -Objekt wird berechnet als
s [0]*31^(n-1) + s [1]*31^(n-2) + ... + s [n-1]
Unter Verwendung von Int -Arithmetik, wobei S [i] das ITH -Zeichen der Zeichenfolge ist, ist n die Länge der Zeichenfolge, und ^ zeigt die Exponentiation an. (Der Hash -Wert der leeren Zeichenfolge ist Null.) "
Berechnen Sie den ASCII -Code jedes Zeichens an die Leistung n - 1 und fügen Sie ihn dann hinzu. Es ist zu sehen, dass die Sonne bei der Implementierung von HashCode sehr streng ist. Dies kann den gleichen Hashcode in den beiden verschiedenen Saiten im größten Teil vermeiden.
Das Konzept des Bucket wird in der Hash -Tabellen -Implementierung von Oracle verwiesen. Wie in der Abbildung unten gezeigt:
Wie aus der obigen Abbildung ersichtlich ist, entspricht die Hash -Tabelle mit Eimer ungefähr einer Kombination aus einer Hash -Tabelle und einer verknüpften Liste. Das heißt, eine verknüpfte Liste wird an jedem Eimer aufgehängt, und jeder Knoten der verknüpften Liste wird zum Speichern von Objekten verwendet. Java verwendet die HashCode () -Methode, um zu bestimmen, welcher Eimer ein Objekt gefunden werden sollte, und sucht es dann in der entsprechenden verknüpften Liste. Wenn Ihre HashCode () -Methode robust genug ist, hat jeder Eimer nur einen Knoten, wodurch die Zeitkomplexität des Suchvorgangs konstanter Ebene erreicht wird. Das heißt, egal in welchem Speicherstück Ihr Objekt platziert ist, ich kann den Bereich sofort über Hashcode () finden, ohne von Anfang bis Ende zu durchqueren und zu suchen. Dies ist auch die Hauptanwendung von Hash -Tabellen.
wie:
Wenn wir die Put (Object O) -Methode von Hashset nennen, werden wir zuerst im entsprechenden Eimer gemäß dem Rückgabewert von O.HashCode () im entsprechenden Eimer lokalisiert. Wenn es keine Knoten im Eimer gibt, dann geben Sie hier o. Wenn es bereits Knoten gibt, hängen Sie O am Ende der verknüpften Liste. In ähnlicher Weise lokalisiert Java beim Aufrufen (Objekt O) den entsprechenden Eimer über den Rückgabewert von HashCode () und ruft dann die Equals () -Methode auf, um sich an den Knoten in der entsprechenden verknüpften Liste zu ermitteln, um festzustellen, ob das Objekt im Knoten das gewünschte Objekt ist.
Verwenden wir ein Beispiel, um diesen Prozess zu erleben:
Erstellen wir zuerst zwei neue Codiererobjekte:
Codierer C1 = neuer Codierer ("Bruce", 10); Codierer C2 = neuer Codierer ("Bruce", 10);Angenommen, wir haben die Equals () -Methode des Codierers neu geschrieben, ohne die HashCode () -Methode neu zu schreiben:
@Override public boolean Equals (Objekt Andere) {System.out.println ("Equals -Methode aufgerufen!"); if (other == this) return true; if (! (Andere Instanz des Codierers)) return false; Codierer o = (Coder) Andere; return o.name.equals (name) && o.age == Alter; }Dann konstruieren wir ein Hashset und legen das C1 -Objekt in den Satz:
Set <Coder> set = new Hashset <Codierer> (); set.add (c1);
Wieder ausführen:
System.out.println (set.contains (c2));
Wir erwarten, dass die entsprechende (C2) -Methode wahr zurückgibt, aber tatsächlich kehrt sie falsch zurück.
Der Name und das Alter von C1 und C2 sind gleich. Warum rufe ich auf (C2) und gibt falsch zurück, nachdem ich C1 in einen Hashset eingelegt habe? Dies ist der HashCode (), der Probleme verursacht. Da Sie die HashCode () -Methode nicht neu schreiben, wenn Hashset nach C2 sucht, wird sie in verschiedenen Eimern nach dem Aspekt gesucht. Wenn C1 beispielsweise in den Eimer 05 eingebaut wird, wird es bei der Suche nach C2 im Eimer 06 gesucht, so dass es natürlich nicht gefunden werden kann. Daher ist der Zweck unseres Umschreibens von HashCode (), dass, wenn A.equals (b) true zurückgibt, der HashCode () von A und B denselben Wert zurückgeben sollte.
Bitten Sie HashCode (), jedes Mal eine feste Zahlenlinie zurückzugeben
Jemand könnte es so umschreiben:
@Override public int HashCode () {return 10; }Wenn dies der Fall ist, verlieren HashMap, Hashset und andere Sammelklassen ihre "Hash -Bedeutung". In den Worten von <effektivem Java> entartet die Hash -Tabelle zu einer verknüpften Liste. Wenn HashCode () jedes Mal die gleiche Zahl zurückgibt, werden alle Objekte in denselben Eimer platziert. Jedes Mal, wenn Sie eine Suchoperation durchführen, durchquert sie die verlinkte Liste, die die Funktion des Hashings vollständig verliert. Es ist also besser, einen robusten Hashcode () als gute Idee zu bieten.
Das obige ist die detaillierte Einführung dieses Artikels zum Umschreiben von HashCode () und Equals () Methoden. Ich hoffe, es wird für alle hilfreich sein. Interessierte Freunde können weiterhin auf andere verwandte Themen auf dieser Website verweisen. Wenn es Mängel gibt, hinterlassen Sie bitte eine Nachricht, um darauf hinzuweisen. Vielen Dank an Freunde für Ihre Unterstützung für diese Seite!