In each class that overrides the Equals method, you must also overwrite the Hashcode method. Otherwise, it violates the general conventions of Object.hashcode, which prevents the class from working properly with all hash-based collections, including Hashmap,hashset,hashtbale.
Hashcode agreed content:
1. The Hashcode method must return the same integer as long as the information used to compare the object's Equals method has not been modified and is called multiple times on the same object. During multiple executions of the same application, each execution of the returned integer can be inconsistent.
2. If two objects are equal according to the Equals (object) method, then the Hashcode return value of the two objects is the same.
3. If two objects are unequal according to the Equals (object) method, the Hashcode return values of the two objects do not necessarily vary, but the performance of the hash table can be improved by producing distinct integer results for different objects.
Consider:
Public classPhoneNumber {Private Final intAreaCode; Private Final intprefix; Private Final intlinenumber; PublicPhoneNumber (intAreaCode,intPrefixintlinenumber) {Rangecheck (AreaCode,999, "area code"); Rangecheck (prefix,999, "prefix"); Rangecheck (linenumber,9999, "line number"); This. AreaCode =AreaCode; This. prefix =prefix; This. linenumber =linenumber; } Private Static voidRangecheck (intArgintMax, String name) { if(Arg < 0 | | arg >max) { Throw NewIllegalArgumentException (name + ":" +Arg); }} @Override Public Booleanequals (Object o) {if(O = = This) return true; if(! (OinstanceofPhoneNumber)) return false; PhoneNumber PN=(PhoneNumber) o; returnPn.linenumber = =linenumber&& Pn.prefix = =prefix&& Pn.areacode = =AreaCode; } }
Run the following code:
New Hashmap<phonenumber, string>(); Map.put (new PhoneNumber (707, 867, 5309), "Jenny"); System.out.println (Map.get(new PhoneNumber (707, 867, 5309)));
We expect it to return to Jenny, but it returns NULL.
The reason is that violates the Hashcode convention, because PhoneNumber does not overwrite the Hashcode method, causes two equal instances to have the unequal hash code, the put method puts the telephone number object in a hash bucket, The Get method looks for the owner of the phone number from another hash bucket and is obviously not available.
This problem can be fixed by overwriting the hashcode and complying with the Convention.
A good hash function tends to "generate unequal hash codes for unequal objects", with the following simple workaround:
1. Save a nonzero constant value, such as 17, in a variable of type int named result. (The hash value is affected by the initial field computed in 2.a for a hash value of 0)
2. For each key field F in the object, complete the steps:
A. Calculating the hash code for the int type C for the field
I. If the field is Boolean, calculate (f? 1:0)
II. If the field is a byte, char, short, or int type, the calculation (int) F
III. If the field is long, calculate (int) (f ^ (f >>> 32))
Iv. If the field is float, calculate float.floattointbits (f)
V. If the field is double, calculate double.doubletolongbits (f), and then
Vi. If the domain is an object reference, and the Equals method of the class compares the domain recursively by calling equals, the hashcode is also called recursively for the domain. If more complex comparisons are needed, a "paradigm" is computed for the domain and then called hashcode for this "paradigm." If the value of the field is null, 0 (or some other constant, but usually 0) is returned.
VII. If the field is an array, then each element is treated as a separate domain, that is, to recursively apply the above rules, calculate a hash code for each important element, and then combine the hash values according to 2.b. If each element in the array field is important, you can use one of the array.hashcode methods that is added in 1.5.
B. Follow the formula below to merge the hash code C computed in step 2.a into result:
result = * result + C. (select 31 is because it is an odd prime number, if the multiplier is even, the multiplication overflow will lose information, the VM can be optimized to * i = = (i << 5)-i)
3. Return result.
After writing the Hashcode method, write unit tests to verify that the same instance has an equal hash code.
Apply the above workarounds to the PhoneNumber class:
@Override Public int hashcode () { int; * result + AreaCode; * result + prefix; * result + linenumber; return result;}
Now, using the previous test code, I found that I was able to return Jenny.
If a class is immutable and the cost of calculating the hash code is large, you should consider caching the hash code inside the object instead of recalculating the hash code every time the request is made, and if most of these objects are used as hash keys, you should calculate the hash code when the instance is created, or you can choose to defer the initialization of the hash code.
Note: Do not attempt to improve performance by excluding key portions of an object from the hash code calculation. Although this may be faster to run, but the effect is not good, when you have a large number of instances, the ignored domain differences are still very large, but the hash function still maps them into the same hash bucket, such as Java 1.2 implemented before the string hash function to check up to 16 characters, For large collections such as URLs, the hash function behaves in a pathological way (mapping the 16th word specifier a very large URL to the same hash bucket, making the collision rate very high and performance degraded).
9th: Overwrite equals when always overwrite Hashcode