Die Basisklasse Object von Java bietet einige Methoden, darunter die Methode equal(), mit der ermittelt wird, ob zwei Objekte gleich sind, und die Methode hashCode(), mit der der Hash-Code des Objekts berechnet wird. Weder equal() noch hashCode() sind endgültige Methoden und können überschrieben werden.
In diesem Artikel werden einige Probleme vorgestellt, die bei der Verwendung und Umschreibung der beiden Methoden beachtet werden müssen.
1. equal()-Methode
Die equal()-Methode in der Object-Klasse ist wie folgt implementiert:
public boolean equals(Object obj) { return (this == obj); }
Aus dieser Implementierung ist ersichtlich, dass die Implementierung der Object-Klasse den Algorithmus mit dem höchsten Differenzierungsgrad übernimmt. Das heißt, solange die beiden Objekte nicht dasselbe Objekt sind, muss equal() false zurückgeben .
Obwohl wir die Methode equal() beim Definieren einer Klasse überschreiben können, gibt es einige Vorsichtsmaßnahmen, die beim Implementieren der Methode equal() beachtet werden sollten:
(1) Reflexivität: x.equals(x) muss true zurückgeben.
(2) Symmetrie: Die Rückgabewerte von x.equals(y) und y.equals(x) müssen gleich sein.
(3) Transitivität: x.equals(y) ist wahr und y.equals(z) ist auch wahr, dann muss x.equals(z) wahr sein.
(4) Konsistenz: Wenn sich die von den Objekten x und y in equal() verwendeten Informationen nicht ändern, bleibt der Wert von x.equals(y) immer unverändert.
(5) Nicht null: x ist nicht null und y ist null, dann muss x.equals(y) falsch sein.
2. hashCode()-Methode
1. Objekt-hashCode()
Die hashCode()-Methode in der Object-Klasse wird wie folgt deklariert:
public native int hashCode();
Es ist ersichtlich, dass hashCode() eine native Methode ist und der Rückgabewerttyp tatsächlich eine Ganzzahl ist. Diese native Methode konvertiert das Objekt. Die Adresse im Speicher wird als Hash-Code zurückgegeben, wodurch sichergestellt wird, dass der Rückgabewert für verschiedene Objekte unterschiedlich ist.
Ähnlich wie die Methode equal() kann die Methode hashCode() überschrieben werden. Das JDK erklärt die Funktion der Methode hashCode() und die Vorsichtsmaßnahmen bei der Implementierung:
(1) hashCode() funktioniert in einer Hash-Tabelle, wie z. B. java.util.HashMap.
(2) Wenn sich die vom Objekt in equal() verwendeten Informationen nicht geändert haben, bleibt der Wert hashCode() immer unverändert.
(3) Wenn zwei Objekte mithilfe der Methode equal() als gleich beurteilt werden, sollte auch die Methode hashCode() gleich sein.
(4) Wenn zwei Objekte mit der Methode equal() als ungleich beurteilt werden, ist hashCode() nicht erforderlich und muss ungleich sein. Entwickler sollten sich jedoch darüber im Klaren sein, dass ungleiche Objekte unterschiedliche hashCode erzeugen können von Hash-Tabellen.
2. Die Rolle von hashCode()
Im Allgemeinen funktioniert hashCode() in Hash-Tabellen wie HashSet, HashMap usw.
Wenn wir ein Objekt zu einer Hash-Tabelle hinzufügen (z. B. HashSet, HashMap usw.), rufen wir zuerst die Methode hashCode() auf, um den Hash-Code des Objekts zu berechnen Lokalisieren Sie das Objekt direkt in der Hash-Tabelle (im Allgemeinen der Modulus des Hash-Codes zur Größe der Hash-Tabelle). Wenn sich an dieser Position kein Objekt befindet, können Sie das Objekt direkt an dieser Position einfügen. Wenn sich an dieser Position Objekte befinden (es können mehrere vorhanden sein, die über eine verknüpfte Liste implementiert werden), rufen Sie die Methode equal() auf, um zu vergleichen, ob Diese Objekte sind gleich dem Objekt. Wenn sie gleich sind, müssen Sie das Objekt nicht speichern. Wenn sie nicht gleich sind, fügen Sie das Objekt der verknüpften Liste hinzu.
Dies erklärt auch, warum equals() gleich ist, dann muss hashCode() gleich sein. Wenn zwei Objekte equal() gleich sind, sollten sie nur einmal in der Hash-Tabelle erscheinen (z. B. HashSet, HashMap usw.); wenn hashCode() nicht gleich ist, werden sie an verschiedenen Stellen in gehasht Hash-Tabelle Position, kommt mehr als einmal in der Hash-Tabelle vor.
Tatsächlich enthält das geladene Objekt in der JVM drei Teile im Speicher: Objektheader, Instanzdaten und Füllung. Darunter enthält der Objektheader einen Zeiger auf den Objekttyp und MarkWord, und MarkWord enthält nicht nur die GC-Generierungsaltersinformationen und Sperrstatusinformationen des Objekts, sondern auch den Hashcode des Objekts ; Die Objektinstanzdaten sind die tatsächlich vom Objekt gespeicherten Informationen. Der Auffüllteil dient nur als Platzhalter, da HotSpot erfordert, dass die Startadresse des Objekts ein ganzzahliges Vielfaches von 8 Bytes sein muss.
Der relevante Implementierungscode in der String-Klasse lautet wie folgt:
private final char value[]; private int hash; // Default to 0 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
Die folgenden Punkte sind aus dem Code ersichtlich:
1. String-Daten sind endgültig, das heißt, sobald ein String-Objekt erstellt wurde, können sie nicht in der Form von String s geändert werden = "hello"; s = "world"; Anweisung: Wenn s = "world" ausgeführt wird, ändert sich der Wert des String-Objekts nicht in "world", sondern es wird ein neues String-Objekt erstellt und die s-Referenz zeigt auf das neue Objekt.
2. Die String-Klasse speichert das Ergebnis von hashCode() als Hash-Wert zwischen, um die Leistung zu verbessern.
3. Die Bedingung dafür, dass das String-Objekt equal() gleich ist, ist, dass beide String-Objekte die gleiche Länge haben und nicht unbedingt dasselbe Objekt sein müssen.
4. Die HashCode()-Berechnungsformel von String lautet: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[ n -1]
Die Hauptgründe für die Verwendung der Zahl 31 im Berechnungsprozess von hashCode() sind folgende:
1、使用质数计算哈希码,由于质数的特性,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小。
2、使用的质数越大,哈希冲突的概率越小,但是计算的速度也越慢;31是哈希冲突和性能的折中,实际上是实验观测的结果。
3、JVM会自动对31进行优化:31 * i == (i << 5) - i
本节先介绍重写hashCode()方法应该遵守的原则,再介绍通用的hashCode()重写方法。
1、重写hashcode()的原则
通过前面的描述我们知道,重写hashCode需要遵守以下原则:
(1)如果重写了equals()方法,检查条件“两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等”是否成立,如果不成立,则重写hashCode ()方法。
(2)hashCode()方法不能太过简单,否则哈希冲突过多。
(3)hashCode()方法不能太过复杂,否则计算复杂度过高,影响性能。
2、hashCode()重写方法
《Effective Java》中提出了一种简单通用的hashCode算法
A、初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;
B、选取equals方法中用于比较的所有域(之所以只选择equals()中使用的域,是为了保证上述原则的第1条),然后针对每个域的属性进行计算:
(1) 如果是boolean值,则计算f ? 1:0
(2) 如果是byte\char\short\int,则计算(int)f
(3) 如果是long值,则计算(int)(f ^ (f >>> 32))
(4) 如果是float值,则计算Float.floatToIntBits(f)
(5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int
(6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0
(7) 如果是数组,那么需要为每个元素当做单独的域来处理。java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上。
C、最后,把每个域的散列码合并到对象的哈希码中。
下面通过一个例子进行说明。在该例中,Person类重写了equals()方法和hashCode()方法。因为equals()方法中只使用了name域和age域,所以hashCode()方法中,也只计算name域和age域。
对于String类型的name域,直接使用了String的hashCode()方法;对于int类型的age域,直接用其值作为该域的hash。
public class Person { private String name; private int age; private boolean gender; public Person() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean isGender() { return gender; } public void setGender(boolean gender) { this.gender = gender; } @Override public boolean equals(Object another) { if (this == another) { return true; } if (another instanceof Person) { Person anotherPerson = (Person) another; if (this.getName().equals(anotherPerson.getName()) && this.getAge() == anotherPerson.getAge()) { return true; } else { return false; } } return false; } @Override public int hashCode() { int hash = 17; hash = hash * 31 + getName().hashCode(); hash = hash * 31 + getAge(); return hash; } }
推荐教程:java教程
Das obige ist der detaillierte Inhalt vonequal()-Methode und hashCode()-Methode (ausführliche Einführung). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!