In this tutorial, you’ll learn everything about equals() and hashCode() in Java — what they are, why we override them, and how they impact HashMap and other collection classes.

We’ll also explore how HashMap works internally using these two methods and cover important interview questions with examples.


1. Why Do We Need equals() and hashCode()?

When you store objects in a collection like HashSet or HashMap, Java needs a way to determine if two objects are equal. That’s where equals() and hashCode() come in:

  • equals() → Checks if two objects are logically equal.
  • hashCode() → Returns a hash value (integer) that represents the object’s memory bucket for quick access.

By default, both are inherited from Object class:


public boolean equals(Object obj);
public int hashCode();

2. The Contract Between equals() and hashCode()

Whenever you override equals(), you must also override hashCode(). This ensures consistent behavior inside hash-based collections like HashMap and HashSet.

Contract Rules:

  • If two objects are equal (equals() returns true), their hashCode() must be the same.
  • If two objects have the same hashCode(), they may or may not be equal.
  • If equals() returns false, hashCode() can be same or different.

3. Example Without Overriding equals() and hashCode()


class Student {
    int id;
    String name;

    Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class WithoutOverrideExample {
    public static void main(String[] args) {
        Student s1 = new Student(101, "Ravi");
        Student s2 = new Student(101, "Ravi");

        System.out.println(s1.equals(s2)); // false
    }
}

Output: false

👉 Even though both objects look identical, equals() from Object compares memory addresses — so they’re not equal.


4. Example With equals() and hashCode() Overridden


class Student {
    int id;
    String name;

    Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && name.equals(student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

public class WithOverrideExample {
    public static void main(String[] args) {
        Student s1 = new Student(101, "Ravi");
        Student s2 = new Student(101, "Ravi");

        System.out.println(s1.equals(s2)); // true
        System.out.println(s1.hashCode() == s2.hashCode()); // true
    }
}

Output:
true
true

✅ Now both objects are logically equal and produce the same hash code.


5. equals() and hashCode() in Real-World: HashMap Example

When you store objects as keys in a HashMap, Java uses both methods internally to find or update entries.


class Employee {
    int id;
    String name;

    Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee)) return false;
        Employee emp = (Employee) o;
        return id == emp.id && name.equals(emp.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

public class HashMapExample {
    public static void main(String[] args) {
        Map<Employee, String> map = new HashMap<>();

        Employee e1 = new Employee(1, "Amit");
        Employee e2 = new Employee(1, "Amit");

        map.put(e1, "Developer");
        map.put(e2, "Tester");

        System.out.println(map.size()); // 1
        System.out.println(map.get(e1)); // Tester
    }
}

Output:
1
Tester

✅ Because both keys are logically equal (same id and name), HashMap replaces the old value.


6. How HashMap Works Internally (Step-by-Step)

Understanding how HashMap uses hashCode() and equals() internally is one of the most common interview questions. Let’s break it down:

Step 1: Compute hash code

When you call put(key, value), Java first calls hashCode() on the key object.

Step 2: Find bucket index

It calculates a bucket index using a hashing algorithm: index = hash(key.hashCode()) % capacity

Step 3: Check for existing key

If the bucket is empty → insert new node.
If not empty → compare existing key using equals().

Step 4: Collision handling

If two keys have the same hash code, HashMap stores them in the same bucket using a linked list or a tree (since Java 8).

Step 5: Treeification

When a bucket’s size exceeds 8 elements, it converts the linked list into a balanced tree to improve lookup performance.

Step 6: get(key) operation

When you call map.get(key), Java again uses hashCode() to locate the bucket and equals() to find the exact match.


7. Visual Representation of HashMap Working


Put(e1, "Developer")
 ├─ hashCode() → 123
 ├─ Bucket[123]
 └─ stores Node(key=e1, value="Developer")

Put(e2, "Tester")
 ├─ hashCode() → 123 (same)
 ├─ Checks equals(e1, e2) → true
 └─ Replaces "Developer" with "Tester"

✅ That’s why overriding both methods correctly is crucial when using objects as keys.


8. Common Mistakes Developers Make

  • Overriding equals() but not hashCode().
  • Using mutable fields (like name) in hash calculation — changing them breaks retrieval.
  • Assuming same hash code means equal objects — not always true.
  • Using identity-based equality when logical equality is needed.

9. Interview Questions and Answers

  1. Q: What is the difference between equals() and hashCode()?
    A: equals() checks logical equality; hashCode() provides the hash bucket location.
  2. Q: What happens if you override equals() but not hashCode()?
    A: Hash-based collections may treat equal objects as different keys.
  3. Q: Can two unequal objects have the same hashCode()?
    A: Yes, that’s called a collision.
  4. Q: What data structures use equals() and hashCode()?
    A: HashMap, HashSet, LinkedHashMap, ConcurrentHashMap.
  5. Q: Why should fields used in equals() also be used in hashCode()?
    A: To maintain consistency — both should define equality in the same way.
  6. Q: What happens internally when you put a key-value pair in a HashMap?
    A: HashMap calculates hash → finds bucket → uses equals() for match → replaces or adds new node.
  7. Q: What is a hash collision?
    A: When two keys have the same hash code but are not equal.
  8. Q: What is treeification in HashMap?
    A: When a bucket’s linked list becomes longer than 8 nodes, it’s converted into a red-black tree.
  9. Q: Why are HashMap keys immutable?
  10. A: Changing key values changes hashCode(), breaking retrieval.
  11. Q: Is HashMap thread-safe
    A: No. Use ConcurrentHashMap for thread-safe operations.

10. Summary Table

Aspectequals()hashCode()
PurposeLogical equalityBucket identification
Return Typebooleanint
Default BehaviorCompares memory addressesGenerates unique hash based on memory
Override RequiredYes (for custom equality)Yes (if equals overridden)
Used InComparing objectsHashMap, HashSet lookup

Conclusion

🎯 In summary, equals() and hashCode() form the foundation of Java’s hashing-based data structures. If you override one, always override the other to maintain consistency and ensure correct behavior in collections like HashMap and HashSet.

Next, you can explore “How HashSet works internally” — which follows the same principles under the hood.