You must override
Here is the contract, adapted from the
When the
If two objects are equal according to the
If two objects are unequal according to the
hashCode
in every class that overrides equals
. If you fail to do so, your class will violate the general contract for hashCode
, which will prevent it from functioning properly in collections such as HashMap
and HashSet
. Here is the contract, adapted from the
Object
specification .When the
hashCode
method is invoked on an object repeatedly during an execution of an application, it must consistently return the same value, provided no information used in equals
comparisons is modified. This value need not remain consistent from one execution of an application to another.If two objects are equal according to the
equals(Object)
method, then calling hashCode
on the two objects must produce the same integer result.If two objects are unequal according to the
equals(Object)
method, it is not required that calling hashCode
on each of the objects must produce distinct results. However, the programmer should be aware that producing distinct results for unequal objects may improve the performance of hash tables.
The key provision that is violated when you fail to override
hashCode
is the second one: equal objects must have equal hash codes. Two distinct instances may be logically equal according to a class’s equals
method, but to Object
’s hashCode
method, they’re just two objects with nothing much in common. Therefore, Object
’s hashCode
method returns two seemingly random numbers instead of two equal numbers as required by the contract.
Map<PhoneNumber, String> m = new HashMap<>();
m.put(new PhoneNumber(707, 867, 5309), "Jenny");
m.put(new PhoneNumber(707, 867, 5309), "Jenny");
At this point, you might expect
m.get(new PhoneNumber(707
, 867
, 5309))
to return "Jenny"
, but instead, it returns null
. Notice that two PhoneNumber
instances are involved: one is used for insertion into the HashMap
, and a second, equal instance is used for (attempted) retrieval. The PhoneNumber
class’s failure to override hashCode
causes the two equal instances to have unequal hash codes, in violation of the hashCode
contract.
. Even if the two instances happen to hash to the same bucket, the
get
method will almost certainly return null
, because HashMap
has an optimization that caches the hash code associated with each entry and doesn’t bother checking for object equality if the hash codes don’t match.
// The worst possible legal hashCode implementation - never use!
@Override public int hashCode() { return 42; }
@Override public int hashCode() { return 42; }
// Typical hashCode method
@Override public int hashCode() {
int result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
return result;
}
@Override public int hashCode() {
int result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
return result;
}
// One-line hashCode method - mediocre performance
@Override public int hashCode() {
return Objects.hash(lineNum, prefix, areaCode);
}
@Override public int hashCode() {
return Objects.hash(lineNum, prefix, areaCode);
}
// hashCode method with lazily initialized cached hash code
private int hashCode; // Automatically initialized to 0
@Override public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
hashCode = result;
}
return result;
}
private int hashCode; // Automatically initialized to 0
@Override public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
hashCode = result;
}
return result;
}
Do not be tempted to exclude significant fields from the hash code computation to improve performance.
Don’t provide a detailed specification for the value returned by
hashCode
, so clients can’t reasonably depend on it; this gives you the flexibility to change it.
In summary, you must override
hashCode
every time you override equals
, or your program will not run correctly. Your hashCode
method must obey the general contract specified in Object
and must do a reasonable job assigning unequal hash codes to unequal instances. This is easy to achieve, if slightly tedious, using the recipe on page 51. As mentioned in Item 10, the AutoValue framework provides a fine alternative to writing equals
and hashCode
methods manually, and IDEs also provide some of this functionality.
No comments:
Post a Comment