在任何重写了equals方法的类里面,重写hashCode方法.如果没有很好的重写 hashcode 方法,将会在涉及到Hash码的应用中出现问题,比如:HashMap、HashSet、Hashtable。
java.lang.Object中,hashcode方法用有如下的特性:
在一次应用中,同一个对象返回的hashcode总是保持一致的。
如果两个对象满足equals(object)方法,那么这两个对象的hashcode也应该保持一致。
但是请注意,没有这样的要求:如果两个对象不满足equals(object)方法,那么这两个对象的hashcode不能够相等。
在override hashcode的过程中,经常在上面提到的第二点遇到困难。
一个好的hash函数应该尽量满足:不同的对象的hashcode是不同的。
下面给出一个近似hash函数的设计。
1 存储一个int类型的非零常数在变量result中,比如17.
2 针对对象中的每一个field,计算它们对应的hashcode:
a boolean :计算(f?0:1)
byte,char,short,int: 计算(int)f
long :计算(int)(f^(f>>>32)
float: 计算Float.floatToIntBits(f)
double: 计算Double.doubleToLongBits(f),然后再对得到的long类型结果计算(int)(f^(f>>>32)
复杂类型的引用,递归调用hashCode函数
数组类型的引用,对数组中的每一个元素应用上面的方法计算hashcode,然后加起来。
b 将a中得到的结果,与result相加,比如: result=37*result+c;
3 返回result。
当然,可以根据实际情况,将一些field排除在hashCode对考虑范围,比如,在equals方法中没有考虑
的field,不应该考虑到hashCode中来。
在上面的b方法中,之所以使用37*result+c来计算,是将field对顺序考虑进来。比如对于一个类似于
String的对象而言,通常情况下,我们要求"abc"和"acb"应该返回不同的HashCode,所以应该将field
的顺序也考虑在内,否则,"abc"和"acb"返回的HashCode将会是相等的。选择37是因为它是一个奇质
数,当然也可以选择其他的奇质数。
如果某个类的某些Field在应用中很少变动,而且这些Field都属于HashCode要计算的范围,并且为这
些类计算HashCode是一个比较耗费时间的事情,可以考虑将这些类的HashCode进行缓存。可以采用一种
Lazy initialize的方式,当这个实例的HashCode()第一次被调用时,计算HashCode,然后保存在该
实例的Field中,比如:
private volatile int hashCode =0;
public int hashCode() {
if (hashCode == 0) {
int result = 17;
//***
hashCode = result;
}
return hashCode;
}
最后的建议,不要为了提高计算HashCode的效率,而将关键的Field排除在外。
Item 8 Always override hashcode when you overri...
"标题:"Item 8 Always override hashcode when you override equals
|
最新讨论 · · · · · · (全部)
推荐看原版(气定神闲)
Item 9 重写toString方法(Butler)
Item 6 :按照常规去覆盖equals方法(Butler)
Item 5 :消除无用的对象实例(Butler)
Item 4:避免创造不必要的对象引用(reference)(Butler)
> 我来回应