Java Reference
In-Depth Information
s.contains(new Name("Mickey", "Mouse")));
}
}
Solution 57: What's in a Name?
A
Name
instance consists of a first name and a last name. Two
Name
instances are equal, as
computed by the
equals
method, if their first names are equal and their last names are equal. First
names and last names are compared using the
equals
method defined in
String
. Two strings are
equal if they consist of the same characters in the same order. Therefore, two
Name
instances are
equal if they represent the same name. For example, the following method invocation returns
true
:
new Name("Mickey", "Mouse").equals(new Name("Mickey", "Mouse"))
The
main
method of the program creates two
Name
instances, both representing Mickey Mouse. The
program puts the first instance into a hash set and then checks whether the set contains the second.
The two
Name
instances are equal, so it might seem that the program should print
true
. If you ran it,
it almost certainly printed
false
. What is wrong with the program?
The bug is that
Name
violates the
hashCode
contract. This might seem strange, as
Name
doesn't even
have a
hashCode
method, but that is precisely the problem. The
Name
class overrides the
equals
method, and the
hashCode
contract demands that equal objects have equal hash codes. To fulfill
this contract,
you must override
hashCode
whenever you override
equals
[EJ Item 8].
Because it fails to override
hashCode
, the
Name
class inherits its
hashCode
implementation from
Object
. This implementation returns an
identity-based
hash code. In other words, distinct objects
are likely to have unequal hash values, even if they are equal.
Name
does not fulfill the
hashCode
contract, so the behavior of a hash set containing
Name
elements is unspecified.
When the program puts the first
Name
instance into the hash set, the set puts an entry for this
instance into a hash bucket. The set chooses the hash bucket based on the hash value of the
instance, as computed by its
hashCode
method. When it checks whether the second
Name
instance is
contained in the hash set, the program chooses which bucket to search based on the hash value of
the second instance. Because the second instance is distinct from the first, it is likely to have a
different hash value. If the two hash values map to different buckets, the
contains
method will
return
false
: The beloved rodent is in the hash set, but the set can't find him.
Suppose that the two
Name
instances map to the same bucket. Then what? All
HashSet
implementations that we know of have an optimization in which each entry stores the hash value of
its element in addition to the element itself. When searching for an element, the implementation
selects the appropriate hash bucket and traverses its entries, comparing the hash value stored in
Search WWH ::
Custom Search