When I use an object as a key in a Hashtable
,
what in the Object
class must I override and why?
Whenyou create your own key object for use in a Hashtable
,you must override the Object.equals()
and Object.hashCode()
methodssince Hashtable
usesa
combination of the key's hashCode()
andequals()
methodsto
store and retrieve its entries quickly. It's also a general rule that whenyou override equals()
,you always override hashCode()
.
Moreon why
A slightly more in-depth explanation will help you understandHashtable
'smechanism for storage and retrieval. A Hashtable
internallycontains
buckets in which it stores the key/value pairs. The Hashtable
usesthe key's hashcode to determine to which bucket the key/value pair should map

Figure 1. AHashtable and its buckets
Figure1 shows a Hashtable
andits buckets. When
you pass a key/value to the Hashtable
,it queries the key's hashcode. TheHashtable
usesthat
code to determine the bucket in which to place the key/value. So, forexample, if the hashcode equals zero, theHashtable
placesthe
value into Bucket 0. Likewise, if the hashcode is two, the Hashtable
placesthe value into Bucket 2. (This is
a simplistic example; the Hashtable
willmassage the hashcode first so theHashtable
doesn'ttry
to insert the value outside the bucket.)
By using the hashcode this way, the Hashtable
canalso
quickly determine in which bucket it has placed the value when you try toretrieve it.
Hashcodes, however, represent only half the picture. The hashcodeonly tells the Hashtable
intowhich
bucket to drop the key/value. Sometimes, however, multiple objects maymap to the same bucket, an event known as a collision. In Java, the Hashtable
respondsto
a collision by placing multiple values into the same bucket (otherimplementations may handle collisions differently). Figure 2 shows what a Hashtable
mightlook
like after a few collisions.

Figure 2. AHashtable after a few collisions
Nowimagine that you call get()
witha key that maps
to Bucket 0. The Hashtable
willnow need to peform a sequential search through the key/value pairs in Bucket
0to find your requested value. To perform this lookup, the Hashtable
executesthe following steps:
1. Query the key's hashcode
2. Retrieve the list of key/valuesresiding in the bucket given by the hashcode
3. Scan through each entrysequentially until a key that equals the
key passed into get()
isfound
Along answer to a short question I know, but it gets worse. Properly overriding equals()
and hashCode()
isa
nontrivial exercise. You must take extreme care to guarantee both methods'contracts.
On implementing equals()
According to the equals()
Javadoc,the method must
conform to the following rules:
"The equals()
method implements an equivalence relation:
· It is reflexive: For anyreference value x, x.equals(x)
shouldreturn
true
· It is symmetric: For anyreference values x and y, x.equals(y)
shouldreturn
true if and only if y.equals(x)
returnstrue
· It is transitive: For anyreference values x, y, and z, if x.equals(y)
returnstrue
and y.equals(z)
returnstrue, then x.equals(z)
shouldreturn
true
· It is consistent: For anyreference values x and y, multiple invocations
of x.equals(y)
consistentlyreturn true or consistently return false, provided
no information used inequals comparisons on the object is modified
· For any non-null reference valuex, x.equals(null)
shouldreturn
false
In Effective Java, JoshuaBloch offers a five-step recipe for writing an effective equals()
method.
Here's the recipe in code form:
publicclass EffectiveEquals {
private int valueA;
private int valueB;
. . .
public boolean equals( Object o ) {
if(this == o) { // Step 1: Perform an == test
return true;
}
if(!(o instanceof EffectiveEquals)){ // Step
2:Instance of check
return false;
}
EffectiveEquals ee = (EffectiveEquals) o;//
Step3: Cast argument
// Step 4: For each important field,check to
see ifthey are equal
// For primitives use ==
// For objects use equals() but be sureto also
// handle the null case first
return ee.valueA == valueA &&
ee.valueB == valueB;
}
. . .
}
Note: Youneed not perform a null check since null
instanceof EffectiveEquals
willevaluate to false.
Finally, for Step 5, go back to equals()
'scontract and ask yourself if the equals()
methodis
reflexive, symmetric, and transitive. If not, fix it!
On implementing hashCode()
For hashCode()
'sgeneral contract, the Javadoc says:
· "Whenever it is invoked onthe same object more than once during an execution
of a Java application, the hashCode()
methodmust consistently return the same
integer, provided no information used inequals comparisons on the object is modified. This integer need not remainconsistent from one execution of an application to another execution of thesame application.
· If two objects are equalaccording to the equals(Object)
method,then
calling the hashCode()
methodon each of the two objects must produce the same
integer result.
· It is not required that if twoobjects are unequal according to theequals(java.lang.Object
method,then
calling the hashCode()
methodon each of the two objects must produce distinct
integer results. However, theprogrammer should be aware that producing distinct integer results for unequalobjects may improve the performance of hashtables."
Creatinga properly working hashCode()
methodproves
difficult; it becomes even more difficult if the object in question isnot immutable. You can calculate a hashcode for a given object in many ways.The most effective method bases the number upon the object's fields. Moreover,you can combine these values in
various ways. Here are two popular approaches:
· You can turn the object's fieldsinto a string, concatenate the strings, and return the resulting hashcode
· You can add each field's hashcodeand return the result