很多时候,会有覆盖 equals 的时候。此类需求多出现在使用领域对象的时候。项目里曾经做过多次。有时候是为了功能,有的时候是为了使用方便。
虽然很多时候开发者可以在需要判断使用自己的防范对对象值进行判断。但是另外一些时候这么做非常不方便。
在 List 的中间保存数据的时候,有时需要将放入 List 的数据 remove ,或者查找指定对象在 List 里的位置,此时Collections 里的实现会调用 equals 方法判断两个对象是否相同。
当自己覆盖了 equals 的时候,有时候是出于缺乏经验,有时是偷懒,没有同时覆盖 hashCode 方法。
按照 java api 中 hashCode() 的定义,是需要同时实现这两个方法的。
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#hashCode()
摘自java.lang.Object规范:
l 在同应用程序执行期间,对同一个对象调用hashCode(),必须传回相同的整数结果 — 前提是equals()所比较的信息都不曾被改动过。至于同一个应用程序在不同执行期所得的呼叫结果,无需一致。
l 如果两个对象被equals(Object)函数视为相等,那么对这两个对象调用hashCode()必须获得相同的整数结果。
l 如果两个对象被equals(Object)函式视为不相等,那么对这两个对象调用hashCode()不必产生不同的整数结果。然而程序员应该意识到,对不同对象产生不同的整数结果,有可能提升hash table 的效率。
生成 hash code 的算法。
最简单的:
public int hashCode(){
return 42;
}
它是合法的,因为它保证相等的对象具有相同的hash 码。但它也是差劲的,因为它使每个对象都具有相同的hash 码,这会造成每㆒个对象都被hashing 置入同㆒个hash bucket,致使hash tables 蜕变为linked lists(译注:如果考虑rehashing机制,还是能够打散开来)。原本只需线性执行时间,如今却需要㆓次方执行时间,这对大型hash table而言会影响程序的实用性。
稍微复杂一点的实现:
1. 将一个非0常数,例如17,储存于int result变量㆗。
2. 对对象中的每㆒个有意义的字段f(更确切㆞说是被equals()所考虑的每一个字段)进行如㆘处理:
A. 对这个字段计算出型别为int的hash 码 c:
i. 如果字段是个boolean,计算(f ? 0 : 1)。
ii. 如果字段是个byte,char,short或int,计算(int)f。
iii. 如果字段是个long,计算(int)(f^(f >>> 32))。
iv. 如果字段是个float,计算Float.floatToIntBits(f)。
v. 如果字段是个double,计算Double.doubleToLongBits(f),然后将计算结果按步骤2.A.iii处理。
vi. 如果字段是个object reference,而且class 的equals()透过「递归呼叫equals()」的方式来比较这一字段,那么就同样也对该字段递归呼叫hashCode()。如果需要更复杂的比较,请对该字段运算㆒个标准表述式(canonical representation),并对该标准表述式呼叫hashCode()。如果字段值是null,就传回0(或其它常数;传回0 是传统做法)。
vii. 如果字段是个array,请将每个元素视为独立字段。也就是说对每一个有意义的元素施行上述规则,用以计算出hash 码,然后再依步骤2.B将这些数值组合起来。
B. 将步骤A计算出来的hash码 c按下列公式组合到变量result中:result = 37*result + c;
3. 传回result。
4. 完成hashCode()之后,反躬自省一下:是否相等的实体具有相等的hash 码?如果不是,找出原因并修正问题。
public int hashCode() {
int result = 17;
result = 37*result + areaCode;
result = 37*result + exchange;
result = 37*result + extension;
return result;
}
-- 摘自《effective java programming language guide》