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
![图片](https://i-blog.csdnimg.cn/blog_migrate/2c933e67102c4242f12ecaa8ddbfdf4b.jpeg)
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.
![图片](https://i-blog.csdnimg.cn/blog_migrate/c9394fa31b43284c4e4ffdc3912340e4.jpeg)
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