Java System.identityHashCode, Object.hashCode und Object.equals verstehen
1. Der Vertrag equals()
Die Methode equals(Object) wird verwendet, um das aktuelle Objekt mit einem anderen Objekt basierend auf den Werten der Eigenschaften jedes Objekts zu vergleichen. Sie können diese Methode in Ihrer Klasse überschreiben.
public boolean equals(Object other)
Beispiel: Die Klasse Money mit 2 Eigenschaften: currencyCode & amount. Zwei Objekte Money werden von der Methode equals() als gleich betrachtet, wenn sie denselben currencyCode und amount haben:
Money.java
package org.o7planning.equals.ex;
import java.util.Objects;
public class Money {
private String currencyCode;
private int amount;
public Money(String currencyCode, int amount) {
this.amount = amount;
this.currencyCode = currencyCode;
}
public int getAmount() {
return amount;
}
public String getCurrencyCode() {
return currencyCode;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Money)) {
return false;
}
Money o = (Money) other;
return this.amount == o.amount //
&& Objects.equals(this.currencyCode, o.currencyCode);
}
}
Wenn Sie die Methode equals() überschreiben, müssen Sie die folgenden Kriterien erfüllen, sie werden als Vertrag equals() bezeichnet:
1 | Reflexive (Reflexiv) | Ein Objekt muss sich selbst entsprechen. |
2 | Symmetric (Symmetrisch) | x.equals(y) muss dasselbe Wert wie y.equals(x) zurückgeben. |
3 | Transitive (Transitiv) | Wenn x.equals(y) und y.equals(z) , dann auch x.equals(z). |
4 | Consistent (Konsistent) | Der Wert von x.equals(y) ändert sich nicht, wenn sich die am Vergleich beteiligten Property nicht ändern. (Zufälligkeit ist nicht erlaubt). |
Symmetric
Die Symmetrie von equals() muss gewährleistet sein, dh wenn x.equals(y) dann y.equals(x) ist. Das klingt einfach, aber manchmal verletzt man es unbeabsichtigt.
Zum Beispiel verletzt die folgende Klasse WrongVoucher die Symmetrie des Vertrags equals():
WrongVoucher.java
package org.o7planning.equals.ex;
import java.util.Objects;
public class WrongVoucher extends Money {
private String store;
public WrongVoucher(String store, String currencyCode, int amount) {
super(currencyCode, amount);
this.store = store;
}
public String getStore() {
return store;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof WrongVoucher)) {
return false;
}
WrongVoucher o = (WrongVoucher) other;
return this.getAmount() == o.getAmount() //
&& Objects.equals(this.getCurrencyCode(), o.getCurrencyCode()) //
&& Objects.equals(this.store, o.store);
}
}
Auf den ersten Blick scheinen die Klasse WrongVoucher und ihre Methode equals() korrekt zu sein. Es funktioniert perfekt, wenn Sie 2 Objekte WrongVoucher vergleichen, aber Sie werden Probleme sehen, wenn Sie Objekte WrongVoucher mit Objekte Money vergleichen und umgekehrt.
WrongVoucherTest.java
package org.o7planning.equals.ex;
public class WrongVoucherTest {
public static void main(String[] args) {
Money m = new Money("USD", 100);
WrongVoucher wv = new WrongVoucher("Chicago S1", "USD", 100);
System.out.println("m.equals(wv): " + m.equals(wv)); // true
System.out.println("wv.equals(m): " + wv.equals(m)); // false
}
}
Output:
m.equals(wv): true
wv.equals(m): false
Um die obige Falle zu vermeiden, können wir die Klasse Voucher neu schreiben und Money als Property verwenden, anstatt von Money zu erben.
Voucher.java
package org.o7planning.equals.ex;
import java.util.Objects;
public class Voucher {
private String store;
private Money money;
public Voucher(String store, String currencyCode, int amount) {
this.store = store;
this.money = new Money(currencyCode, amount);
}
public String getStore() {
return store;
}
public Money getMoney() {
return money;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Voucher)) {
return false;
}
Voucher o = (Voucher) other;
return Objects.equals(this.store, o.store) //
&& this.money.equals(o.money);
}
}
2. System.identityHashCode(Object)
In Java gibt die statische Methode System.identityHashCode(obj) den identity hashcode des Objekts obj zurück, der eine nicht negative Integer zwischen [0, 2^31-1] ist. Der Identity hashcode eines Objekt null ist 0.
@HotSpotIntrinsicCandidate
public static native int identityHashCode(Object x);
Gemäß der Designidee sollte der identity hashcode verschiedener Objekte unterschiedlich sein. Dies ist jedoch nicht absolut garantiert. Der Algorithmus von JVM kann nur garantieren, dass die Wahrscheinlichkeit eines doppelten identity hashcode sehr gering ist. Der identity hashcode eines Objekts wird nur im ersten Moment der tatsächlichen Verwendung berechnet und im Header des Objekts gespeichert.
Identity hashcode wird sicherlich nicht basierend auf der Adresse des Objekts im Speicher generiert. Leider gibt es keine Dokumentation über den Generierungsalgorithmus für Identity hashcode. Das Geheimnis liegt im Quellcode der JVM, der in C++ geschrieben wurde. Ich werde diesen Algorithmus aktualisieren, wenn weitere Informationen verfügbar sind.
3. Object.hashcode()
Die Methode hashCode() der Klasse java.lang.Object gibt den hashcode des aktuellen Objekts zurück, der genau der identity hashcode dieses Objekts ist.
public class Object {
public int hashCode() {
return System.identityHashCode(this);
}
}
Beispiel für hashcode und identity hashcode eines reinen Objekts (new java.lang.Object()).
HashCodeEx1.java
package org.o7planning.hashcode.ex;
public class HashCodeEx1 {
public static void main(String[] args) {
Object obj1 = new Object();
int idHashcode = System.identityHashCode(obj1);
int hashcode = obj1.hashCode();
System.out.println("Identity Hashcode: " + idHashcode);
System.out.println("Hashcode: " + hashcode);
}
}
Output:
Identity Hashcode: 1651191114
Hashcode: 1651191114
Nachkommende Klassen von java.lang.Object können die Methode hashCode() überschreiben, um einen benutzerdefinierten Wert zurückzugeben, müssen jedoch die folgenden Regeln sicherstellen, die auch als Vertrag hashCode() bezeichnet werden.
1 | Equals consistency | Wenn zwei Objekte gemäß der Methode equals(Object) gleich sind, muss ihre Methode hashCode() denselben Wert zurückgeben. |
2 | Internal consistency | Der Wert von hashCode() kann sich nur ändern, wenn sich die an der Methode equals(Object) beteiligten Eigenschaften ändern. |
Zwei Objekte, die gemäß der Methode equals(Object) nicht gleich sind, haben nicht unbedingt unterschiedliche hashcode. Zwei verschiedene Objekte mit unterschiedlichen Werten hashcode verbessern jedoch die Leistung der Hash table (Weitere Erläuterungen finden Sie im Artikel über HashMap und HashSet).
mehr sehen:
- Die Anleitung zu Java HashSet
HashCodeEx2.java
package org.o7planning.hashcode.ex;
public class HashCodeEx2 {
public static void main(String[] args) {
Employee tom = new Employee("Tom");
Employee jerry = new Employee("Jerry");
System.out.println("Employee: " + tom.getFullName());
System.out.println(" - Identity hashcode: " + System.identityHashCode(tom));
System.out.println(" - Hashcode: " + tom.hashCode());
System.out.println("\nEmployee: " + jerry.getFullName());
System.out.println(" - Identity hashcode: " + System.identityHashCode(jerry));
System.out.println(" - Hashcode: " + jerry.hashCode());
}
}
class Employee {
private String fullName;
public Employee(String fullName) {
this.fullName = fullName;
}
public String getFullName() {
return this.fullName;
}
@Override
public int hashCode() {
if (this.fullName == null || this.fullName.isEmpty()) {
return 0;
}
char ch = this.fullName.charAt(0);
return (int) ch;
}
}
Output:
Employee: Tom
- Identity hashcode: 1579572132
- Hashcode: 84
Employee: Jerry
- Identity hashcode: 359023572
- Hashcode: 74
4. Die Konsistenz hashCode() & equals() verletzen
Grundsätzlich müssen Sie, wenn Ihre Klasse die Methode equals(Object) überschreibt, auch die Methode hashCode() überschreiben, um sicherzustellen, dass 2 Objekte, die nach der Methode equals(Object) gleich sind, denselben hashcode haben. Dies ist notwendig und sicher, wenn Sie das Objekt dieser Klasse als Schlüssel von *HashMap (HashMap, WeakHashMap, IdentityHashMap,...) verwenden.
Die folgende Klasse BadTeam verletzt Equals consistency (Gleichheitskonsistenz):
BadTeam.java
package org.o7planning.equals.ex;
import java.util.Objects;
public class BadTeam {
private String name;
private int numberOfMembers;
public BadTeam(String name, int numberOfMembers) {
this.name = name;
this.numberOfMembers = numberOfMembers;
}
public String getName() {
return name;
}
public int getNumberOfMembers() {
return numberOfMembers;
}
@Override
public boolean equals(Object other) {
if(this == other) {
return true;
}
if(!(other instanceof BadTeam)) {
return false;
}
BadTeam o = (BadTeam) other;
return Objects.equals(this.name, o.name);
}
@Override
public int hashCode() {
return this.numberOfMembers;
}
}
BadTeamTest.java
package org.o7planning.equals.ex;
public class BadTeamTest {
public static void main(String[] args) {
BadTeam team1 = new BadTeam("Team 1", 3);
BadTeam team2 = new BadTeam("Team 1", 5);
boolean isEquals = team1.equals(team2); // true
int hashcode1 = team1.hashCode(); // 3
int hashcode2 = team2.hashCode(); // 5
System.out.println("team1.equals(team2): " + isEquals); // true
System.out.println("hashcode1 == hashcode2: " + (hashcode1 == hashcode2)); // false
}
}
Output:
team1.equals(team2): true
hashcode1 == hashcode2: false
Wenn Sie die Klasse *HashMap(HashMap, WeakHashMap, IdentityHashMap,..) verwenden, kann dies zu einer Verletzung des Vertrag hashCode() führen. Die Dinge funktionieren möglicherweise nicht so, wie Sie es erwarten.
HashMap_BadTeam_Test.java
package org.o7planning.equals.ex;
import java.util.HashMap;
public class HashMap_BadTeam_Test {
public static void main(String[] args) {
// BadTeam team --> String leader.
HashMap<BadTeam, String> map = new HashMap<>();
BadTeam team1 = new BadTeam("Team 1", 3);
BadTeam team2 = new BadTeam("Team 1", 5);
map.put(team1, "Tom");
map.put(team2, "Jerry");
BadTeam team = new BadTeam("Team 1", 10);
String leader = map.get(team);
System.out.println("Leader of " + team.getName() + " is " + leader);
}
}
Output:
Leader of Team 1 is null
Sehen Sie sich auch an, wie HashMap, WeakHashMap und IdentityHashMap dieDaten speichern, um mehr zu verstehen, was oben erwähnt wurde:
Java Grundlagen
- Anpassen von Java-Compiler, der Ihre Annotation verarbeitet (Annotation Processing Tool)
- Java Programmierung für Team mit Eclipse und SVN
- Die Anleitung zu Java WeakReference
- Die Anleitung zu Java PhantomReference
- Komprimierung und Dekomprimierung in Java
- Konfigurieren von Eclipse zur Verwendung des JDK anstelle von JRE
- Java-Methoden String.format() und printf()
- Syntax und neue Funktionen in Java 8
- Die Anleitung zu Java Reguläre Ausdrücke
- Die Anleitung zu Java Multithreading Programming
- JDBC Driver Bibliotheken für verschiedene Arten von Datenbank in Java
- Die Anleitung zu Java JDBC
- Holen Sie sich die automatisch erhöhenden Wert der Spalte bei dem Insert eines Rekord, der JDBC benutzt
- Die Anleitung zu Java Stream
- Die Anleitung zu Java Functional Interface
- Einführung in Raspberry Pi
- Die Anleitung zu Java Predicate
- Abstrakte Klasse und Interface in Java
- Zugriffsmodifikatoren (Access modifiers) in Java
- Die Anleitung zu Java Enum
- Die Anleitung zu Java Annotation
- Vergleichen und Sortieren in Java
- Die Anleitung zu Java String, StringBuffer und StringBuilder
- Die Anleitung zu Java Exception
- Die Anleitung zu Java Generics
- Manipulieren von Dateien und Verzeichnissen in Java
- Die Anleitung zu Java BiPredicate
- Die Anleitung zu Java Consumer
- Die Anleitung zu Java BiConsumer
- Was ist erforderlich, um mit Java zu beginnen?
- Geschichte von Java und der Unterschied zwischen Oracle JDK und OpenJDK
- Installieren Sie Java unter Windows
- Installieren Sie Java unter Ubuntu
- Installieren Sie OpenJDK unter Ubuntu
- Installieren Sie Eclipse
- Installieren Sie Eclipse unter Ubuntu
- Schnelle lernen Java für Anfänger
- Geschichte von Bits und Bytes in der Informatik
- Datentypen in Java
- Bitweise Operationen
- if else Anweisung in Java
- Switch Anweisung in Java
- Schleifen in Java
- Die Anleitung zu Java Array
- JDK Javadoc im CHM-Format
- Vererbung und Polymorphismus in Java
- Die Anleitung zu Java Function
- Die Anleitung zu Java BiFunction
- Beispiel für Java Encoding und Decoding mit Apache Base64
- Die Anleitung zu Java Reflection
- Java-Remote-Methodenaufruf - Java RMI
- Die Anleitung zu Java Socket
- Welche Plattform sollten Sie wählen für Applikationen Java Desktop entwickeln?
- Die Anleitung zu Java Commons IO
- Die Anleitung zu Java Commons Email
- Die Anleitung zu Java Commons Logging
- Java System.identityHashCode, Object.hashCode und Object.equals verstehen
- Die Anleitung zu Java SoftReference
- Die Anleitung zu Java Supplier
- Java Aspect Oriented Programming mit AspectJ (AOP)
Show More
- Anleitungen Java Servlet/JSP
- Die Anleitungen Java Collections Framework
- Java API für HTML & XML
- Die Anleitungen Java IO
- Die Anleitungen Java Date Time
- Anleitungen Spring Boot
- Anleitungen Maven
- Anleitungen Gradle
- Anleitungen Java Web Services
- Anleitungen Java SWT
- Die Anleitungen JavaFX
- Die Anleitungen Oracle Java ADF
- Die Anleitungen Struts2 Framework
- Anleitungen Spring Cloud