equals() and hashCode()
Logical equality, the hashCode contract and how HashMap and HashSet use both methods internally.
#Introduction
Among all Java interview topics, equals() and hashCode() are some of the most frequently asked, especially for developers with 3–10 years of experience.
Almost every collection based on hashing depends on these two methods:
HashMapHashSetHashtableLinkedHashMapConcurrentHashMap
Understanding them is essential for writing correct Java applications.
#Why Do We Need equals()?
Suppose we have two Employee objects:
Employee e1 = new Employee(101, "John");
Employee e2 = new Employee(101, "John");Logically, both employees represent the same person. But by default:
System.out.println(e1.equals(e2));falseWhy? Because the default implementation of equals() compares memory addresses, not object data.
#Default Implementation
Inside the Object class:
public boolean equals(Object obj) {
return (this == obj);
}It simply checks whether both references point to the same object.
#Overriding equals()
Most business objects should compare their state, not their memory address.
import java.util.Objects;
class Employee {
private int id;
private String name;
Employee(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
return id == other.id &&
Objects.equals(name, other.name);
}
}Now:
Employee e1 = new Employee(101, "John");
Employee e2 = new Employee(101, "John");
System.out.println(e1.equals(e2));true#Steps for Writing equals()
Always follow this sequence:
this == objobj == nullgetClass()orinstanceof- Type casting
- Compare fields
This pattern is widely accepted and easy to understand.
#Understanding hashCode()
hashCode() returns an integer. This integer is used to decide where an object should be stored inside hash-based collections.
Employee e = new Employee(101, "John");
System.out.println(e.hashCode());16273829The exact value is not important. What matters is that equal objects must produce the same hash code.
#Why Does Java Need Hash Codes?
Imagine a library with one million books. Without numbering, you must search every shelf. With numbering, you go directly to the correct section.
Similarly, HashMap uses:
#Internal Working of HashMap
When inserting map.put(key, value); Java performs:
During retrieval, map.get(key); Java repeats the same process.
Bucket Example
Suppose Employee(101) has hashCode = 13, and the table has 5 buckets: 13 % 5 = 3. The object is stored in Bucket 3.
#Hash Collision
Different objects can end up in the same bucket. Example (10 buckets): Employee A has hashCode = 25 and Employee B has hashCode = 35. Since 25 % 10 = 5 and 35 % 10 = 5, both land in Bucket 5.
This is called a hash collision. Java then uses equals() to identify the correct object.
#equals() and hashCode() Contract
Java defines a contract between these methods.
Rule 1
If a.equals(b) returns true, then a.hashCode() == b.hashCode() must also be true.
Rule 2
If a.equals(b) returns false, their hash codes may be equal or different.
Rule 3
The same object should consistently return the same hash code while its state remains unchanged.
#Correct Implementation
import java.util.Objects;
class Employee {
private int id;
private String name;
Employee(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Employee))
return false;
Employee other = (Employee) obj;
return id == other.id &&
Objects.equals(name, other.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}#What Happens If You Override Only equals()?
HashSet<Employee> set = new HashSet<>();
set.add(new Employee(101, "John"));
set.add(new Employee(101, "John"));
System.out.println(set.size());Expected 1, but possible output:
2Reason: different hash codes place the objects in different buckets.
#What Happens If You Override Only hashCode()?
Objects may land in the same bucket, but equals() still compares references, so duplicates are not recognized correctly.
#Objects.equals() vs equals()
Instead of:
name.equals(other.name);use:
Objects.equals(name, other.name);Why? It safely handles null.
Objects.equals(null, null)trueNo NullPointerException.
#instanceof vs getClass()
Using instanceof — accepts subclasses:
if (!(obj instanceof Employee))Using getClass() — requires exactly the same runtime class:
if (getClass() != obj.getClass())Choose based on your equality requirements.
#How IDEs Help
Modern IDEs can generate both methods automatically. Generated code usually uses Objects.equals() and Objects.hash(), which are clean and reliable.
#Real Project Examples
Entity Classes
Employee, Customer, Order, Product — typically compared using a unique identifier.
DTO Classes
Useful when comparing request or response objects.
Cache Keys
Objects used as cache keys must implement equals() and hashCode() correctly.
#Interview Questions
Q1Why should equals() and hashCode() be overridden together?
Because hash-based collections depend on both methods to locate and compare objects correctly.
Q2Can two unequal objects have the same hash code?
Yes. This is called a hash collision.
Q3Can two equal objects have different hash codes?
No. That violates the Java contract.
Q4Which method is called first in HashMap?
hashCode(). After finding the bucket, equals() is used if necessary.
Q5Can hashCode() return a negative number?
Yes. Java allows negative hash codes.
Q6Why isn't hashCode() guaranteed to be unique?
Because an int has a limited range, while the number of possible objects is effectively unlimited.
Q7Why does HashSet rely on hashCode()?
To quickly locate the correct bucket before checking equality.
Q8Can immutable objects safely cache their hash code?
Yes. Since their state never changes, the hash code remains valid.
#Common Mistakes
- Overriding
equals()withouthashCode(). - Comparing strings using
==. - Ignoring
nullchecks. - Using mutable fields in
hashCode()when objects are stored in aHashMaporHashSet.
#Best Practices
- Always override
equals()andhashCode()together. - Use
Objects.equals()for object comparisons. - Use
Objects.hash()for generating hash codes. - Prefer immutable fields in equality calculations.
- Keep implementations simple, consistent, and deterministic.
#Quick Revision
#Chapter Summary
After completing this chapter, you should be able to:
- Explain the purpose of
equals()andhashCode(). - Describe the contract between the two methods.
- Understand how
HashMapandHashSetuse hashing internally. - Correctly implement
equals()andhashCode()for domain objects. - Answer common interview questions and identify incorrect implementations during code reviews.