9:覆盖equals时总要覆盖hashcode
1.hashcode 通用约定
1.在应用程序执行期间,只要对象的 equals 方法的比较嘈杂所用到的信息没有被修改,那么对这同一个对象调用多次 hashcode 方法都必须始终如一的返回同一个函数。在同一个应用程序的多次执行过过程中,每次执行所返回的整数可以不一致。
2.如果两个对象根据 equals 方法的毕竟是相等的,那么调用这两个对象中任意一个对象的 hashcode 方法都必须产出同样的整数结果。
3.如果两个对象根据 equals 方法比较是不相等的,那么调用这两个对象中任意一个对象的 hashcode 方法,产生的结果可以相同也可以不同。但是你应该知道,给不相等的对象产生不一样的 hash 值,有可能提高散列表的性能。
2.尽可能给不相等的对象产生不一样的 hash 值的简单方法
1.把某个非零的常数值,比如17,保存在一个名为 result 的 int 类型变量中。
2.对于对象中每个关键域 f(指 equals 方法中涉及的每个域),完成以下步骤:
a.为该域计算 int 类型的散列码c:
如果该域是 boolean 类型,计算 (f ? 1 : 0)。
如果该域是 byte、char、short 或者 int 类型,计算 (int)f。
如果该域是 long 类型,计算 (int)(f ^ (f >>> 32))。
如果该域是 float 类型,计算 Float.floatToIntBits(f)。
如果该域是 double 类型,计算 Double.doubleToLongBits(f),然后按照上面第三步,为得到的 long 类型值计算散列值。
如果该域是一个对象引用,并且该类的 equals 方法通过递归的调用 equals 的方式来比较这个域,则同样为这个域递归的调用 hashcode。如果需要更复杂d额比较,则为这个域计算一个”范式“,然后针对这个范式调用 hashcode。如果这个域的值为 null,则返回0。
如果该域s回一个数组,则要吧每一个元素当做单独的域来处理。也就是说,递归地应用上述规则。如果数组中的每个元素都很重要,可以利用 Arrays.hashCode 方法。
b.按照下面的公式,把 a 中计算得到的散列码 c 合并到 result 中:
- result = 31 * result + c;
3.返回 result。
4.写完 hashcode 方法以后,判断一下是否满足三个条件。
在散列码计算过程中,可以把冗余域排除在外。
3.几点注意
- 如果一个类是不可变的,并且计算散列码的开销也比较大,就应该考虑把散列码缓存在对象内部,而不是每次请求都重新计算。
- 不要试图从散列码计算中排除掉一个对象的关键部分来提高性能。