Sagen wir, ich habe die folgende Klasse:
%Vor%Würde dies eine korrekte Implementierung von hashCode sein? Dies ist nicht, wie ich es normalerweise tue (ich neige dazu, Effective Javas Richtlinien zu folgen), aber ich habe immer die Versuchung, einfach so etwas wie den obigen Code zu machen.
Danke
Es hängt davon ab, was Sie mit "richtig" meinen. Angenommen, Sie verwenden hashCode()
aller relevanten equals()
-Definierungsfelder, dann ist es "korrekt". Solche Formeln werden jedoch wahrscheinlich keine gute Verteilung haben und daher wahrscheinlich mehr Kollisionen als sonst verursachen, was sich nachteilig auf die Leistung auswirken wird.
Hier ein Zitat von Effektive Java 2nd Edition , Punkt 9: Überschreiben Sie hashCode
immer, wenn Sie equals
Während das Rezept in diesem Element relativ gute Hash-Funktionen liefert, liefert es keine modernen Hash-Funktionen, und auch Java-Plattform-Bibliotheken bieten keine solchen Hash-Funktionen wie zu Release 1.6. Das Schreiben solcher Hash-Funktionen ist ein Forschungsthema, das am besten den Mathematikern und Informatikern überlassen wird. [... Nichtsdestotrotz sollten die in diesem Artikel beschriebenen Techniken für die meisten Anwendungen geeignet sein.
Es kann nicht sehr viel Rechenleistung erfordern, um zu bewerten, wie gut Ihre vorgeschlagene Hash-Funktion ist, aber warum überhaupt stören? Warum nicht einfach etwas folgen, was sich in der Praxis als anständig erwiesen hat?
int
-Variable mit dem Namen result
. int
hashcode c
für jedes Feld:
boolean
ist, berechnen Sie (f ? 1 : 0)
byte, char, short, int
ist, berechnen Sie (int) f
long
ist, berechnen Sie (int) (f ^ (f >>> 32))
float
ist, berechnen Sie Float.floatToIntBits(f)
double
ist, berechne Double.doubleToLongBits(f)
, dann hasse die resultierende long
wie oben. equals
dieser Klasse das Feld durch rekursives Aufrufen von equals
vergleicht, rufen Sie hashCode
für das Feld rekursiv auf. Wenn der Wert des Felds null
lautet, geben Sie 0 zurück. Arrays.hashCode
-Methoden verwenden. c
in result
wie folgt: result = 31 * result + c;
Nun, natürlich ist dieses Rezept ziemlich kompliziert, aber glücklicherweise müssen Sie es nicht jedes Mal neu implementieren, dank java.util.Arrays.hashCode(Object[])
(und com.google.common.base.Objects
bietet eine praktische Vararg-Variante.
Object.hashCode()
Es ist nicht erforderlich, dass, wenn zwei Objekte ungleich der Methode
equals(java.lang.Object)
sind, der Aufruf der MethodehashCode
für jedes der beiden Objekte eindeutige ganzzahlige Ergebnisse erzeugen muss. Allerdings sollte der Programmierer beachten, dass die Erzeugung von ganzzahligen Ergebnissen für ungleiche Objekte die Leistung von Hashtabellen verbessern kann.
Dies ist nach dem Vertrag zulässig. Aber es gibt immer 1
zurück. In HotSpot gibt es ein Kompilierzeit-Flag, um immer 1
für den Identity Hash-Wert zurückzugeben. Solche Entscheidungen werden jedoch zu einer schlechten Leistung führen.
Es gibt ein besonderes Problem mit der Multiplikation. Ein 0-Hash-Wert von einer Komponente wird nicht nur den Wert vernichten, sondern Potenzen von zwei werden die unteren Bits progressiv auf Null setzen.
Kommutative Operatoren haben das Problem, dass Neuordnungen von Werten einen Konflikt verursachen.
Wenn es eine bestimmte Beziehung zwischen Hash-Werten von Komponenten gibt, ist die Addition besonders schlecht. (4, 6)
und (2, 8)
kollidieren zum Beispiel.
Nein, aber in der Praxis ist das mit ziemlicher Sicherheit keine gute Idee. Am wichtigsten ist, dass Sie keines der Felder, die Sie im Hash-Code verwenden, ändern dürfen. Sie müssen alle konstant sein.
Wenn Sie einen ändern, kann das passieren: Sie fügen die Objectie in einem HashSet ein, Sie ändern ein Feld und testen dann, ob sich das Objekt im HashSet befindet. Obwohl es da drin ist, wird der Hash-Code aufgrund der Tatsache, dass der Hash-Code geändert wurde, nicht gefunden!