Classes that overrides the Object.equals()
method must also override the Object.hashCode()
method. The Java API [[API 2006]] class java.lang.Object
documentation requires that
If two objects are equal according to the
equals(Object)
method, then calling thehashCode
method on each of the two objects must produce the same integer result.
The equals()
method is used to determine logical equivalence between object instances. Consequently, the hashCode()
method must return the same value for all equivalent objects. If the default hashCode()
method returns distinct numbers rather than returning the same value for all members of an equivalence class
However, its contract requires that it return the same value for all members of an equivalence class.
Failure to follow this contract is a common source of defects.
Noncompliant Code Example
This noncompliant code example stores a credit card number into a HashMap
and retrieves it. The expected retrieved value is Java
, however, null
is returned instead. The reason for this erroneous behavior is that the CreditCard
class overrides the equals()
method but fails to override the hashCode()
method. Consequently, the default hashCode()
method returns a different value for each object, even though the objects are logically equivalent; these differing values lead to examination of different hash buckets, which prevents the get()
method from finding the intended value.
public final class CreditCard { private final int number; public CreditCard(int number) { this.number = (short) number; } public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof CreditCard)) { return false; } CreditCard cc = (CreditCard)o; return cc.number == number; } public static void main(String[] args) { Map<CreditCard, String> m = new HashMap<CreditCard, String>(); m.put(new CreditCard(100), "Java"); // Assuming Integer.MAX_VALUE is the largest number for card System.out.println(m.get(new CreditCard(100))); } }
Compliant Solution
This compliant solution shows how the hashCode()
method can be overridden so that the same value is generated for any two instances that compare equal when Object.equals()
is used. Bloch discusses the recipe to generate such a hash function in good detail [[Bloch 2008]].
import java.util.Map; import java.util.HashMap; public final class CreditCard { private final int number; public CreditCard(int number) { this.number = (short) number; } public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof CreditCard)) { return false; } CreditCard cc = (CreditCard)o; return cc.number == number; } public int hashCode() { int result = 7; result = 37 * result + number; return result; } public static void main(String[] args) { Map<CreditCard, String> m = new HashMap<CreditCard, String>(); m.put(new CreditCard(100), "Java"); System.out.println(m.get(new CreditCard(100))); } }
Risk Assessment
Overriding the equals()
method without overriding the hashCode()
method can lead to unexpected results.
Guideline |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
MET13-J |
low |
unlikely |
high |
P1 |
L3 |
Automated Detection
Automated detection of classes that override only one of equals()
and hashcode()
is straightforward. Sound static determination that the implementations of equals()
and hashcode()
are mutually consistent is not feasible in the general case. Heuristic techniques may be useful for the latter issue.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this guideline on the CERT website.
Bibliography
[[API 2006]] Class Object
[[Bloch 2008]] Item 9: Always override hashCode
when you override equals
[[MITRE 2009]] CWE ID 581 "Object Model Violation: Just One of Equals and Hashcode Defined"
MET12-J. Ensure objects that are equated are equatable 05. Methods (MET) MET14-J. Follow the general contract when implementing the compareTo method