重写 equals 方法时同时也要重写 hashcode 方法
在每个类中,在重写 equals 方法的时侯,一定要重写 hashcode 方法。 如果不这样做,你的类违
反了 hashCode 的通用约定,这会阻止它在 HashMap 和 HashSet 这样的集合中正常工作。根据 Object 规
范,以下时具体约定。
1. 如果没有修改 equals 方法中用以比较的信息,在应用程序的一次执行过程中对一个对象重复调用
hashCode 方法时,它必须始终返回相同的值。在应用程序的多次执行过程中,每个执行过程在该
对象上获取的结果值可以不相同。
2. 如果两个对象根据 equals(Object) 方法比较是相等的,那么在两个对象上调用 hashCode 就必须产
生的结果是相同的整数。
3. 如果两个对象根据 equals(Object) 方法比较并不相等,则不要求在每个对象上调用 hashCode 都必
须产生不同的结果。 但是,程序员应该意识到,为不相等的对象生成不同的结果可能会提高散列
表(hash tables)的性能。
当无法重写 hashCode 时,所违反第二个关键条款是:相等的对象必须具有相等的哈希码( hash
codes)。 根据类的 equals 方法,两个不同的实例可能在逻辑上是相同的,但是对于 Object 类的
hashCode 方法,它们只是两个没有什么共同之处的对象。因此, Object 类的 hashCode 方法返回两个
看似随机的数字,而不是按约定要求的两个相等的数字
当无法重写 hashCode 时,所违反第二个关键条款是:相等的对象必须具有相等的哈希码
一个好的 hash 方法趋向于为不相等的实例生成不相等的哈希码。这也正是 hashCode 约定中第三条
的表达。理想情况下,hash 方法为集合中不相等的实例均匀地分配 int 范围内的哈希码。实现这种理想
情况可能是困难的。 幸运的是,要获得一个合理的近似的方式并不难。 以下是一个简单的配方:
1. 声明一个 int 类型的变量 result,并将其初始化为对象中第一个重要属性 c 的哈希码,如下面步
骤 2.a 中所计算的那样。(回顾条目 10,重要的属性是影响比较相等的领域。)
2. 对于对象中剩余的重要属性 f ,请执行以下操作:
a. 比较属性 f 与属性 c 的 int 类型的哈希码:
-- i. 如果这个属性是基本类型的,使用 Type.hashCode(f) 方法计算,其中 Type 类是对
应属性 f 基本类型的包装类。
-- ii. 如果该属性是一个对象引用,并且该类的 equals 方法通过递归调用 equals 来比较该属性,
并递归地调用 hashCode 方法。 如果需要更复杂的比较,则计算此字段的“范式(“canonical
representation)”,并在范式上调用 hashCode。 如果该字段的值为空,则使用 0(也可以使用其
他常数,但通常来使用 0 表示)。
-- iii. 如果属性 f 是一个数组,把它看作每个重要的元素都是一个独立的属性。 也就是说,通
过递归地应用这些规则计算每个重要元素的哈希码,并且将每个步骤 2.b 的值合并。 如果数组没
有重要的元素,则使用一个常量,最好不要为 0。如果所有元素都很重要,则使用
Arrays.hashCode 方法。
b. 将步骤 2.a 中属性 c 计算出的哈希码合并为如下结果: result = 31 * result + c;
3. 返回 result 值。