equals() & hashCode()
TL;DR
equals()andhashCode()form the backbone of Hash-based collections (likeHashMapandHashSet).- The Contract: If two objects are equal according to
equals(), they must return the exact same integer fromhashCode(). - If you override
equals(), you must always overridehashCode(), or else Hash collections will silently fail.
Concept
When you insert an object into a HashSet, Java doesn’t use equals() to check every single item to see if it’s a duplicate (that would be O(N) performance).
Instead, it calls hashCode() on your object to generate an integer (like 12345). It uses this integer to instantly jump to bucket #12345 (O(1) performance).
Once it is inside bucket #12345, it uses equals() to compare your object against any other objects that happen to share the exact same bucket, ensuring true equality.
If you make two Person objects equal because they share the name “Alice”, but you don’t override hashCode(), the JVM assigns them two completely different, random hash codes based on their memory addresses. The HashSet puts them in two different buckets. It never even tries to call equals(). You end up with a Set containing duplicates!
Examples
import java.util.Objects;
public class User {
private String username;
private String email;
public User(String username, String email) {
this.username = username;
this.email = email;
}
// 1. Override equals to define logical equality
@Override
public boolean equals(Object o) {
// Performance optimization: are they the exact same object in memory?
if (this == o) return true;
// Defensive check: is it null or a different class?
if (o == null || getClass() != o.getClass()) return false;
// Cast and compare fields
User user = (User) o;
return Objects.equals(username, user.username) &&
Objects.equals(email, user.email);
}
// 2. MUST override hashCode using the EXACT same fields as equals()
@Override
public int hashCode() {
return Objects.hash(username, email);
}
}
Interview Questions
Q: Can two unequal objects have the same hashCode?
A: Yes! This is called a Hash Collision. Because hashCode() returns a 32-bit int, there are only 4 billion possible hash codes. Since you can create an infinite number of strings, eventually two different strings must generate the same integer (e.g., "FB" and "Ea" both hash to 2236).
This is why HashMap uses buckets. If two different keys generate 2236, the Map places them both inside bucket #2236 (usually as a Linked List). When retrieving the data, it goes to bucket #2236, and uses .equals() to figure out which of the two items is the one you actually wanted.
Q: What happens if hashCode() always returns 1?
A: It is technically legal according to the contract, because all equal objects will return 1.
However, it destroys the performance of your collections. If every object returns 1, the HashMap puts every single object into bucket #1. Your O(1) HashMap degrades into an O(N) LinkedList, and looking up an item requires calling .equals() on every single element.