要重写自己的hashCode方法并没有什么绝对正确的答案,但是我们的目标是:不相等的对象尽可能有不同的hashCode,而且必须满足的一个通用约定是:相等的对象应该具有相同的hashCode。下面介绍一种hashCode的实现方式,这种实现方式对一般的程序来说足够了,至于如何实现更完美的hashCode方法就留给数学家或者理论家去讨论吧。
第一步:定义一个初始值,一般来说取17
int result = 17;
第二步:分别解析自定义类中与equals方法相关的字段(假如hashCode中考虑的字段在equals方法中没有考虑,则两个equals的对象就很可能具有不同的hashCode)
情况一:字段a类型为boolean 则[hashCode] = a ? 1 : 0;
情况二:字段b类型为byte/short/int/char, 则[hashCode] = (int)b;
情况三:字段c类型为long, 则[hashCode] = (int) (c ^ c>>>32);
情况四:字段d类型为float, 则[hashCode] = d.hashCode()(内部调用的是Float.hashCode(d), 而该静态方法内部调用的另一个静态方法是Float.floatToIntBits(d))
情况五:字段e类型为double, 则[hashCode] = e.hashCode()(内部调用的是Double.hashCode(e), 而该静态方法内部调用的另一个静态方法是Double.doubleToLongBits(e),得到一个long类型的值之后,跟情况三进行类似的操作,得到一个int类型的值)
情况六:引用类型,若为null则hashCode为0,否则递归调用该引用类型的hashCode方法。
情况七:数组类型。(要获取数组类型的hashCode,可采用如下方法:s[0]*31 ^ (n-1) + s[1] * 31 ^ (n-2) + ..... + s[n-1], 该方法正是String类的hashCode实现所采用的算法)
第三步:对于涉及到的各个字段,采用第二步中的方式,将其依次应用于下式:
result = result * 31 + [hashCode];
补充说明一点:如果初始值result不取17而取0的话,则对于hashCode为0的字段来说就没有区分度了,这样更容易产生冲突。比如两个自定义类中,一个类比另一个类多出来一个或者几个字段,其余字段全部一样,分别new出来2个对象,这2个对象共有的字段的值全是一样的,而对于多来的那些字段的值正好都是0,并且在计算hashCode时这些多出来的字段又是最先计算的,这样的话,则这两个对象的hashCode就会产生冲突。还是那句话,hashCode方法的实现没有最好,只有更好。