Classes that override the Object.equals()
method must also override the Object.hashCode()
method. The java.lang.Object
class requires that any two objects that compare equal using the equals(Object)
method must produce the same integer result when the hashCode()
method is invoked on the objects [[API 2006]].
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. Failure to follow this contract is a common source of defects.
Noncompliant Code Example
This noncompliant code example associates credit card numbers with strings using a HashMap
and subsequently attempts to retrieve the string value associated with a credit card number. The expected retrieved value is 4111111111111111
; the actual retrieved value is null
. The cause of 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), "4111111111111111"); System.out.println(m.get(new CreditCard(100))); } }
Compliant Solution
This compliant solution overrides the hashCode()
method so that it generates the same value for any two instances that are considered to be equal by the equals()
method. Bloch discusses the recipe to generate such a hash function in 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), "4111111111111111"); System.out.println(m.get(new CreditCard(100))); } }
Risk Assessment
Overriding the equals()
method without overriding the hashCode()
method can lead to unexpected results.
Rule |
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, although heuristic techniques may be used.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="609c7091-4fcc-424f-a0d2-f3337e3901bc"><ac:plain-text-body><![CDATA[ |
[[MITRE 2009 |
AA. Bibliography#MITRE 09]] |
[CWE-581 |
http://cwe.mitre.org/data/definitions/581.html] "Object Model Violation: Just One of Equals and Hashcode Defined" |
]]></ac:plain-text-body></ac:structured-macro> |
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="5f3db2b9-63c5-4d21-9500-f13c355df10c"><ac:plain-text-body><![CDATA[ |
[[API 2006 |
AA. Bibliography#API 06]] |
[Class Object |
http://java.sun.com/javase/6/docs/api/java/lang/Object.html] |
]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="a3743f42-a35f-4773-a0d6-722aeb881410"><ac:plain-text-body><![CDATA[ |
[[Bloch 2008 |
AA. Bibliography#Bloch 08]] |
Item 9: Always override |
]]></ac:plain-text-body></ac:structured-macro> |
MET12-J. Ensure objects that are equated are equatable 05. Methods (MET) MET14-J. Follow the general contract when implementing the compareTo() method