Java Reference
In-Depth Information
Set contains:[100, 200]
Set contains bk1: true
Set contains:[300, 200]
Set contains bk1: false
The program adds two
BadKey
objects called
bk1
and
bk2
to the
Set
. The first line in the output confirms that the
set contains the two objects. Then, the value for the
id
of
bk1
object is changed from 100 to 300, which is confirmed
by the third line in the output. Since you have not removed the object
bk1
from the set, the fourth line of the output is
unexpected. The fourth line of the output states that the object
bk1
does not exist in the set, whereas the third line of
the output states that
bk1
object is in the set.
What's wrong? Is the object
bk1
in the set or not? The answer is that the object
bk1
is in the set until you remove
it. If you use a for-each loop or an iterator to access all objects in the set, you will be able to get to it. However, the
collection (the set in this case) will not be able to find the object
bk1
. The reason why the set is not able to find the
bk1
object is that the hash code value of the object
bk1
changed after it was added to the set. Recall that
HashSet
is
a hash-based collection in Java. It uses the hash code of the object to locate the bucket in which the object will be
placed. When
s.contains(bk1)
is executed the second time, the hash code value of
bk1
will be 300, which is the
returned value from its
hashCode()
method. When the object
bk1
was placed in the set, its hash code was 200. Since
the hash code of the object
bk1
has changed, the set will mistakenly identify a different bucket to locate it. Since the
set is looking for the object
bk1
in a different bucket than the one in which it was placed, it does not find it. Where is
the problem? The problem lies in the
hashCode()
method of the
BadKey
class. The
BadKey
class is a mutable class and
the mutable state of this class (the
id
instance variable) has been used to compute its hash code, which is causing the
problem in locating the object in the set.
One way to fix this problem of apparently losing the
BadKey
objects in the set is to return a constant value from
its
hashCode()
method, say 99. The following is a valid implementation (not a good one, though) of the
hashCode()
method of the
BadKey
class:
// BadKey.java
package com.jdojo.collections;
public class BadKey {
// Other code goes here...
public int hashCode() {
// Return the same value 99 all the time
return 99;
}
}
The above code will fix the problem of losing the object
bk1
in the example shown in Listing 12-36 because hash
code value for an object of the
BadKey
class never changes. However, it introduces another issue that is related to the
performance of the hash-based collection. If you store objects of the
BadKey
class in a hash-based collection, say a
set, all objects will hash to the same bucket because all objects of the
BadKey
class will have the same hash code value,
which is 99. You fixed one problem and introduced another!
The main issue with the
BadKey
class is its mutability. It has only one instance variable
id
that is mutable. You
should consider the following guidelines when you work with mutable objects with hash-based collection:
•
You should avoid using objects of a mutable class as elements in a
Set
and as keys in a
Map
,
if possible. Consider using objects of immutable classes such
String
,
Integer
, or your own
immutable class as keys for a
Map
and elements for a
Set
.