为什么重写 equals
的时候必须重写 hashCode
大家可能从很多教程中了解到:
SUN官方的文档中规定"如果重定义equals方法,就必须重定义hashCode方法,以便用户可以将对象插入到散列(哈希)表中"
那么 SUN 公司是出于什么考虑做了这个规定呢?
在集合框架中的HashSet
,HashTable
和HashMap
都使用哈希表的形式存储数据,而hashCode
计算出来的哈希码便是它们的身份证。哈希码的存在便可以:
- 快速定位对象,提高哈希表集合的性能。
- 只有当哈希表中对象的索引即
hashCode
和对象的属性即equals
同时相等时,才能够判断两个对象相等。 - 从上面可以看出,哈希码主要是为哈希表服务的,其实如果不需要使用哈希表,也可以不重写
hashCode
。但是SUN公司应该是出于对程序扩展性的考虑(万一以后需要将对象放入哈希表集合中),才会规定重写equals
的同时需要重写hashCode
,以避免后续开发不必要的麻烦。
重写equals
的注意事项
Java语言规范要求equals
需要具有如下的特性:
- 自反性:对于任何非空引用 x,
x.equals()
应该返回true
。 - 对称性:对于任何引用 x 和 y,当且仅当
y.equals(x)
返回true
,x.equals(y)
也应该返回true
。 - 传递性:对于任何引用 x、y 和 z,如果
x.equals(y)
返回true
,y.equals(z)
也应返回同样的结果。 - 一致性:如果 x 和 y 引用的对象没有发生变化,反复调用
x.equals(y)
应该返回同样的结果。 - 对于任意非空引用 x,
x.equals(null)
应该返回false
。
在对象比较时,我们应该如何编写出一个符合特性的 equals
方法呢,《Core Java》中提出了如下建议:
- 显式参数命名为 otherObject,稍后将它转换成另一个叫做 other 的变量。
-
检测 this 与 otherObject 是否引用同一个对象:
if (this == otherObject) return true;
计算这个等式可以避免一个个比较类中的域,实现优化。
-
检测 otherObject 是否为 null,如果为 null,返回 false。进行非空校验是十分重要的。
-
比较 this 与 otherObject 是否属于同一个类。
- 如果每个子类都重写了
equals
,使用getClass
检验:
if (getClass() != otherObject.getClass()) return false;
- 如果每个子类都重写了