equals() & hashCode()

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • equals() and hashCode() form the backbone of Hash-based collections (like HashMap and HashSet).
  • The Contract: If two objects are equal according to equals(), they must return the exact same integer from hashCode().
  • If you override equals(), you must always override hashCode(), 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.