为什么重写equals(),就必须要重写hashCode()?

一、equals与hashCode到底是什么?

(1)equals()方法

查看Object的源码可知

public boolean equals(Object obj) {
return (this == obj);
}

Object的equals方法直接判断两个对象在内存中的地址是否相等。

(2)hashCode()方法

查看Object的源码可知

public native int hashCode();

Object的hashCode方法是个本地方法,底层由C++或C实现的,返回对象在内存中的地址的整数形式。


二、我们为什么要用到哈希表

以HashSet集合来说,我们知道存入Set集合中的元素是不能重复的,那么HashSet是怎么判断存入的元素是否重复呢?当我们向HashSet中存入一个元素时,HashSet首先调用对象的hashCode方法,得到一个特定的哈希值,再通过哈希算法将此特定的哈希值经过运算后映射到哈希表中的一个地址上,如果这块地址上之前没有存放任何对象,HashSet就将此对象存入该块地址。如果这块地址上已经有元素存在了,HashSet就会调用对象的equals判断两个对象是否相同,如果返回true,那么此次操作失败,如果返回false,代表两个对象的哈希值虽然相同,但两者通过equals比较并不相同,那么HashSet就会将此对象散列到其他位置。

总而言之,我们重写后的equals一般是比较对象的属性是否相同,那么判断效率显著低于hashCode。好,如果java没有hashCode方法,那么某个集合在判断存入元素是否重复时,会把存入元素与之前的所有元素进行equals判断,假设之前存了10000个元素,那么仅仅执行个判断重复的操作就要执行10000次equals操作,效率低下令人汗颜。

java意识到存在这样的效率低下问题,引入了hashCode方法,可以大大提高判断重复的效率。既然引入了hashCode方法,那么大家在重写hashCode方法的时候,就需要遵守java的约定。


三、equals方法与hashCode方法的契约

Object的hashCode方法上有这样的注释

* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>

上面的大意如下

(1)每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象上的equals比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致。

(2)如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果。

(3)如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果。但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能。


四、重写equals(),就必须要重写hashCode()的理由

但我们重写了equals,拎出对象的几个重要属性进行比较,属性值不同的话返回false。如果,我们这时候不重写hashCode方法,那么hashCode依然返回对象的内存地址的整数形式。就会出现这样的情况,两个对象equals返回true,但hashCode返回的整数并不相同,破坏了源码中对重写hashCode提出的规则,因此,我们重写equals,就必须要重写hashCode。