Understanding Java System.identityHashCode, Object.hashCode and Object.equals
1. The equals() contract
No ADS
equals(Object) method is used to compare the current object with another object based on the values of the properties of each object. You can override this method in your class.
public boolean equals(Object other)
Example: Money class with 2 properties: currencyCode & amount. Two Money objects are considered equal by equals() method if they have the same currencyCode and amount:
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);
}
}
When overriding equals() method, you need to comply with the following criteria, they are called an equals() contract:
1 | Reflexive | An object must equal itself |
2 | Symmetric | x.equals(y) must return the same result as y.equals(x). |
3 | Transitive | if x.equals(y) and y.equals(z) then also x.equals(z). |
4 | Consistent | The value of x.equals(y) does not change if the properties involved in the comparison do not change. (Randomness is not allowed). |
Symmetric
The symmetry of equals() needs to be guaranteed, in other words if x.equals(y) then y.equals(x). This sounds simple, but sometimes you violate it unintentionally.
For example, the WrongVoucher class below violates the symmetry of the equals() contract:
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);
}
}
At first glance, WrongVoucher class and its equals() method appear to be correct. It works perfectly if you compare 2 WrongVoucher objects, but you will see problems if you compare WrongVoucher object with Money object and vice versa.
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
To avoid the above trap, we can rewrite Voucher class and use Money as a property instead of inheriting from Money.
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, System.identityHashCode(obj) static method returns the identity hashcode of the obj object, which is a non-negative integer between [0, 2^31-1]. The identity hashcode of a null object is 0.
@HotSpotIntrinsicCandidate
public static native int identityHashCode(Object x);
According to the design idea, identity hashcode of different objects should be different. However this is not guaranteed absolutely. JVM's algorithm can only guarantee that the probability of duplicate identity hashcode is very small. Identity hashcode of an object is only calculated at the first moment when it is actually used and stored in the object's Header.
Identity hashcode is certainly not generated based on the address of the object in memory. Unfortunately, there is no documentation about the identity hashcode generation algorithm, the secret lies in the source code of the JVM written in C++ language. I will update on this algorithm if more information becomes available.
3. Object.hashcode()
No ADS
hashCode() method of java.lang.Object class returns hashcode of the current object, which is exactly the identity hashcode of that object.
public class Object {
public int hashCode() {
return System.identityHashCode(this);
}
}
Example of hashcode and identity hashcode of a pure object (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
Descendant classes of java.lang.Object can override hashCode() method to return a custom value but need to ensure the following rules, which is also called hashCode() contract .
1 | Equals consistency | If two objects are equal according to equals(Object) method, their hashCode() method must return the same value. |
2 | Internal consistency | The value of hashCode() can change only if the properties participating in equals(Object) method change. |
Two objects that are not equal according to equals(Object) method do not necessarily have different hashcode(s). However, two different objects with different hashcode values will improve the performance of Hash table (See more explanation in the article about HashMap and HashSet).
See more:
- 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. HashCode() & equals() consistency violation
No ADS
Basically, when your class overrides equals(Object) method, you also have to override hashCode() method to make sure that 2 objects that are equal by equals(Object) method will have the same hashcode. This is necessary and safe when you use the object of this class as a key of *HashMap (HashMap, WeakHashMap, IdentityHashMap,...).
The BadTeam class below violates Equals consistency:
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
Violation of hashCode() contract may result when you use *HashMap class (HashMap, WeakHashMap, IdentityHashMap,..). Things may not work as you expect.
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
See also how HashMap, WeakHashMap and IdentityHashMap store data to understand more what was mentioned above:
No ADS
Java Basic
- Data Types in java
- Java PhantomReference Tutorial with Examples
- JDK Javadoc in CHM format
- Java Stream Tutorial with Examples
- Java Predicate Tutorial with Examples
- Java BiConsumer Tutorial with Examples
- Arrays in Java
- JDBC Driver Libraries for different types of database in Java
- Abstract class and Interface in Java
- Java Commons Email Tutorial with Examples
- Install Eclipse
- Bitwise Operations
- Install Eclipse on Ubuntu
- Configuring Eclipse to use the JDK instead of JRE
- Java Commons Logging Tutorial with Examples
- Java Enums Tutorial with Examples
- Loops in Java
- Java Regular Expressions Tutorial with Examples
- Install Java on Ubuntu
- Quick Learning Java for beginners
- Install Java on Windows
- Comparing and Sorting in Java
- Inheritance and polymorphism in Java
- Java Consumer Tutorial with Examples
- Java String, StringBuffer and StringBuilder Tutorial with Examples
- Java Exception Handling Tutorial with Examples
- Example of Java encoding and decoding using Apache Base64
- if else statement in java
- Switch Statement in Java
- Java Supplier Tutorial with Examples
- Java Programming for team using Eclipse and SVN
- Java JDBC Tutorial with Examples
- Java remote method invocation - Java RMI Tutorial with Examples
- Java Multithreading Programming Tutorial with Examples
- Customize java compiler processing your Annotation (Annotation Processing Tool)
- What is needed to get started with Java?
- Java Aspect Oriented Programming with AspectJ (AOP)
- Understanding Java System.identityHashCode, Object.hashCode and Object.equals
- Java Compression and Decompression Tutorial with Examples
- Java Reflection Tutorial with Examples
- Install OpenJDK on Ubuntu
- Java String.format() and printf() methods
- History of Java and the difference between Oracle JDK and OpenJDK
- Introduction to the Raspberry Pi
- Java Socket Programming Tutorial with Examples
- Java Generics Tutorial with Examples
- Manipulating files and directories in Java
- Java WeakReference Tutorial with Examples
- Java Commons IO Tutorial with Examples
- History of bits and bytes in computer science
- Which Platform Should You Choose for Developing Java Desktop Applications?
- Java SoftReference Tutorial with Examples
- Syntax and new features in Java 8
- Java Annotations Tutorial with Examples
- Java Function Tutorial with Examples
- Access modifiers in Java
- Java BiFunction Tutorial with Examples
- Get the values of the columns automatically increment when Insert a record using JDBC
- Java Functional Interface Tutorial with Examples
- Java BiPredicate Tutorial with Examples
Show More
- Java Servlet/Jsp Tutorials
- Java Collections Framework Tutorials
- Java API for HTML & XML
- Java IO Tutorials
- Java Date Time Tutorials
- Spring Boot Tutorials
- Maven Tutorials
- Gradle Tutorials
- Java Web Services Tutorials
- Java SWT Tutorials
- JavaFX Tutorials
- Java Oracle ADF Tutorials
- Struts2 Framework Tutorials
- Spring Cloud Tutorials