In Java, the equals()
and hashCode()
methods are used to define object equality and to enable the effective use of objects in hash-based data structures such as HashMap
, HashSet
, etc. These methods are part of the Object
class, which is the root class for all Java objects. However, they are often overridden in custom classes to provide meaningful equality comparisons and hash code generation.
Understanding the equals() and hashCode() contract in Java is crucial for implementing correct and efficient solutions. Neglecting their proper implementation can lead to significant issues in the applications we develop.
The default implementation of the equals()
method in the Object
class compares objects by their reference. This means that two objects are considered equal if they are the same object in memory. However, this is not always the right solution.
Syntax:
public boolean equals(Object obj) {
return (this == obj);
}
In many cases, this default behavior is not what we want, especially when dealing with custom classes. To correctly compare the content of objects, we need to override the equals() method in our class. Java SE specifies the requirements that our implementation of the equals() method should meet. These criteria are straightforward and aim to ensure consistency.
The equals() method must adhere to the following principles:
Example: We have created a BankAccount class with three attributes.
import java.util.Objects;
public class BankAccount {
private String accountNumber;
private String accountHolderName;
private double balance;
public BankAccount(String accountNumber, String accountHolderName, double balance) {
this.accountNumber = accountNumber;
this.accountHolderName = accountHolderName;
this.balance = balance;
}
// Getters and setters
public static void main(String [] args) {
BankAccount account1 = new BankAccount("Savings", "Test", 2022);
BankAccount account2 = new BankAccount("Savings", "Test", 2022);
System.out.println(account1.equals(account2)); // returns false
}
}
In this example, we have created two objects of BankAccount class in the main method. When we compare both objects using the equals() method it returns a false even after all the three attribute values are same.
Now let’s override the equals method in BankAccount class:
import java.util.Objects;
public class BankAccount {
//Fields And Constructors
// Getters and setters
// Equals method implementation
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
BankAccount otherAccount = (BankAccount) obj;
return Objects.equals(accountNumber, otherAccount.accountNumber)
&& Objects.equals(accountHolderName, otherAccount.accountHolderName)
&& Double.compare(balance, otherAccount.balance) == 0;
}
public static void main(String [] args) {
BankAccount account1 = new BankAccount("Savings", "Test", 2022);
BankAccount account2 = new BankAccount("Savings", "Test", 2022);
System.out.println(account1.equals(account2)); // returns true
}
}
In this example, the equals()
method checks if two BankAccount
objects are equal by comparing their individual attributes using Objects.equals()
for string comparison and Double.compare()
for comparing the balance. Now the equals method returns true when we compare account1 and account2.
The hashCode()
method is used to generate a numeric hash code for an object. This hash code is used by hash-based data structures like HashMap
to quickly locate and manage objects. When we store objects in hash-based collections, the objects are stored based on their hash codes. If two objects have the same hash code, the data structure will use the equals()
method to check for actual equality.
To ensure the proper functioning of hash-based collections, it is crucial to override the hashCode()
method when we override the equals()
method.
Java SE defines a clear contract for the hashCode()
method, and upon careful examination, it becomes evident that hashCode()
and equals()
are closely interrelated. The hashCode()
contract includes three critical criteria, all of which mention the equals()
method:
hashCode()
should only change if a property that is used in the equals()
method changes. In other words, if two objects are equal according to equals()
, they must produce the same hashCode()
.equals()
method must consistently return the same hashCode()
value.hashCode()
. Since the number of possible hash codes is limited (due to the finite number of integers), it’s natural for different objects to produce the same hash code. In such cases, the equals()
method should be used to distinguish between the objects and resolve any potential collisions.Example: Let’s print the hashCode of two objects of BankAccount class as defined in the above example before implementing the hashCode() method:
public static void main(String [] args) {
BankAccount account1 = new BankAccount("Savings", "Test", 2022);
BankAccount account2 = new BankAccount("Savings", "Test", 2022);
System.out.println(account1.equals(account2)); // true
System.out.println(account1.hashCode()); // 168423058
System.out.println(account2.hashCode()); // 821270929
}
In the above example, we can see hashCode of both the equals are different even though both objects are equal. Let’s override the hashCode() method for BankAccount class:
import java.util.Objects;
public class BankAccount {
//Fields And Constructors
// Getters and setters
@Override
public int hashCode() {
int result;
long temp;
result = accountNumber.hashCode();
result = 31 * result + accountHolderName.hashCode();
temp = Double.doubleToLongBits(balance);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
public static void main(String [] args) {
BankAccount account1 = new BankAccount("Savings", "Test", 2022);
BankAccount account2 = new BankAccount("Savings", "Test", 2022);
System.out.println(account1.equals(account2)); // true
System.out.println(account1.hashCode()); // -813975577
System.out.println(account2.hashCode()); // -813975577
}
}
This example shows that after overriding the hashCode() method, both objects have the same hashCode when they are equal.
The hashCode() and equals() contract is a fundamental concept in Java, ensuring the proper functioning of applications when dealing with object comparison and hashing. This contract defines a set of rules that must be followed to maintain consistency and correctness. In essence, the contract specifies that the hashCode() method should return the same hash code value for objects that are considered equal based on the equals() method. Additionally, it emphasizes that if two objects are equal, their hash codes must also be equal.
HashCode serves as a shortcut for optimizing object comparison since it provides a numeric representation of an object’s state. On the other hand, the equals() method is responsible for precisely checking the equality of objects, usually by comparing their content or key properties. By adhering to this contract, developers can ensure that objects behave as expected during operations like searching, adding, or removing elements from collections, making the application efficient and reliable. It prevents unexpected behavior and data inconsistencies that might otherwise arise.