In Java, hash-based collections such as HashMap, HashSet, and Hashtable use the hashCode() method to decide the bucket where an object is stored. After locating the bucket, the equals() method is used to compare objects.
How Hashing Works in Java
- The correct bucket is identified using hashCode().
- The bucket is searched using equals().
Hence, to ensure correct behavior in hash-based collections, both methods must be overridden together and consistently.
Contract Between equals() and hashCode()
As stated by Joshua Bloch (Effective Java): You must override hashCode() in every class that overrides equals().
According to java.lang.Object contract:
- Multiple calls to hashCode() must return the same value if the object state does not change.
- If two objects are equal using equals(), they must return the same hash code.
- Unequal objects may return the same hash code, but returning distinct hash codes improves performance.
Case 1: Overriding Both equals() and hashCode()
When both methods are overridden properly, hash-based collections work as expected, and duplicate keys are avoided.
Example: This program shows why overriding both equals() and hashCode() is necessary when using objects as HashMap keys.
import java.util.*;
class Geek {
String name;
int id;
Geek(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Geek geek = (Geek) obj;
return name.equals(geek.name) && id == geek.id;
}
@Override
public int hashCode() {
return id;
}
}
class GFG {
public static void main(String[] args) {
Geek g1 = new Geek("Ram", 1);
Geek g2 = new Geek("Ram", 1);
Map<Geek, String> map = new HashMap<>();
map.put(g1, "CSE");
map.put(g2, "IT");
for (Geek geek : map.keySet()) {
System.out.println(map.get(geek));
}
}
}
Output
IT
Explanation:
- equals() compares name and id to check logical equality.
- hashCode() returns the same value for equal objects.
- Since g1 and g2 are equal and have the same hash code, the second entry replaces the first.
- Output: only "IT" is printed, showing no duplicate key is created.
Case 2: Overriding Only equals() Method
Here, equals() is overridden but hashCode() is not. This causes Java to place objects in different buckets.
Example: This program shows what happens when only equals() is overridden and hashCode() is not.
import java.util.*;
class Geek {
String name;
int id;
Geek(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Geek geek = (Geek) obj;
return name.equals(geek.name) && id == geek.id;
}
}
class GFG {
public static void main(String[] args) {
Geek g1 = new Geek("Ram", 1);
Geek g2 = new Geek("Ram", 1);
Map<Geek, String> map = new HashMap<>();
map.put(g1, "CSE");
map.put(g2, "IT");
for (Geek geek : map.keySet()) {
System.out.println(map.get(geek));
}
}
}
Output
IT CSE
Explanation:
- equals() considers g1 and g2 equal based on name and id.
- hashCode() is not overridden, so both objects get different hash codes.
- They are stored in different buckets of the HashMap.
- As a result, both values (CSE and IT) are printed, creating duplicate keys.
Case 3: Overriding Only hashCode() Method
Here, hashCode() is overridden but equals() is not. Java compares objects using memory reference, not data.
Example: This program shows what happens when only hashCode() is overridden and equals() is not.
import java.util.*;
class Geek {
String name;
int id;
Geek(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public int hashCode() {
return id;
}
}
class GFG {
public static void main(String[] args) {
Geek g1 = new Geek("Ram", 1);
Geek g2 = new Geek("Ram", 1);
Map<Geek, String> map = new HashMap<>();
map.put(g1, "CSE");
map.put(g2, "IT");
for (Geek geek : map.keySet()) {
System.out.println(map.get(geek));
}
}
}
Output
CSE IT
Explanation:
- hashCode() returns the same value for g1 and g2.
- equals() is not overridden, so reference comparison is used.
- Both objects are treated as different keys.
- As a result, both values (CSE and IT) are printed, causing duplicate entries.