equals() and hashCode() methods are two important methods that work together to determine object equality and are particularly useful when working with collections such as HashSet, HashMap, and Hashtable. Here’s how they are related and how you can properly override them.
Relationship Between equals() and hashCode()
- Contract Between
equals()andhashCode():- If two objects are considered equal according to the
equals()method, they must have the samehashCode(). - However, two objects with the same
hashCode()may not necessarily be equal according toequals(). This is because different objects can have the same hash code (hash collisions).
- If two objects are considered equal according to the
- Why the Contract is Important:
- When using a
HashSet,HashMap, or similar collections, Java first useshashCode()to determine which bucket an object belongs to. Then, if multiple objects share the same hash code (due to a hash collision), it usesequals()to check if the objects are actually equal. - If the contract between
equals()andhashCode()is broken, collections likeHashMapmay not behave as expected.
- When using a
Overriding equals() and hashCode()
If you override equals(), you must override hashCode() to maintain this contract. Here’s an example of how to correctly override them in a class:
Example:
public class Person {
private String name;
private int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Override equals()
public boolean equals(Object obj) {
if (this == obj) return true; // Check if the same reference
if (obj == null || getClass() != obj.getClass()) return false; // Check class equality
Person person = (Person) obj; // Typecast
return age == person.age && name.equals(person.name); // Compare fields
}
// Override hashCode()
public int hashCode() {
// Use prime numbers and multiply the fields for better hash distribution
int result = name.hashCode();
result = 31 * result + age;
return result;
}
// Getters and toString() for better debugging
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
How it Works:
- In the
equals()method, we first check if the objects are the same reference usingthis == obj. Then, we check if the object passed is null or belongs to a different class. If not, we cast it to thePersontype and compare relevant fields (name and age). - In the
hashCode()method, we generate a hash code by combining the hash codes of the fields. The31is a common multiplier used in hash functions because it helps distribute hash codes more evenly.
Key Points:
- Consistency: If two objects are equal (i.e.,
equals()returnstrue), they must have the samehashCode(). - Efficiency: Hash code collisions should be minimized for efficiency, as they degrade performance in hash-based collections.
- Immutable Fields: It’s a good practice to base
hashCode()andequals()on immutable fields (fields that do not change), as changing fields that affecthashCode()can cause issues in hash-based collections.
Example Usage:
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Alice", 30);
Person p2 = new Person("Alice", 30);
Person p3 = new Person("Bob", 25);
System.out.println(p1.equals(p2)); // true
System.out.println(p1.equals(p3)); // false
System.out.println(p1.hashCode() == p2.hashCode()); // true
System.out.println(p1.hashCode() == p3.hashCode()); // false
}
}
Summary:
equals(): Determines whether two objects are logically equivalent.hashCode(): Provides a numeric value to quickly identify objects for use in hash-based collections.- Always override both methods together to ensure correct behavior when working with collections like
HashMaporHashSet.
