为什么重写equals方法要重写hashCode方法
这两个方法都是Object类中的。首先先解释一下这两个方法。上源码吧。
equals()
public boolean equals(Object obj) {
return (this == obj);
}
可以看到,该方法就是判断两个对象是否是同一个对象。底层是使用 == 来实现的。咱们也知道,== 作用于引用类型,比较的是两个对象的内存地址是否相同来判断是否是同一个对象。
hashCode()
public native int hashCode();
可看出这是一个native方法。
那他们两者有什么关系呢?
两者事实关系如下:
- 如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同!!!!;
- 如果两个对象不同(即用equals比较返回false),那么它们的hashCode值可能相同也可能不同;
- 如果两个对象的hashCode相同(存在哈希冲突),那么它们可能相同也可能不同(即equals比较可能是false也可能是true)
- 如果两个对象的hashCode不同,那么他们肯定不同(即用equals比较返回false)
上述的关系,随意百度一下都有。
现在来想一下:在某个类中重写了equals方法,两个对象==返回的是true,如果这时不重写hashCode方法,是不是不能保证这两个对象的hashCode相同,那么是不是就不符合咱们的要求了。或者说不符合规定。
在 源码中咱们可以看到hashCode上面有很多注解。
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <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>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java™ programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
自行百度翻译。其中有一条意思就是,equals方法相同的话,那么hashCode的返回值也得相同。
现在咱们再来想一下:对于对象集合的判重,如果一个集合含有10000个对象实例,仅仅使用equals()方法的话,那么对于一个对象判重就需要比较10000次,随着集合规模的增大,时间开销是很大的。
但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的hashCode不相同,也不再需要调用equals()方法,从而大大减少了equals()比较次数。
从这里想的话,重写equals方法再重写hashCode方法,显然是提高了效率。
对于“为什么重写equals()就一定要重写hashCode()方法?”这个问题应该是有个前提,就是你需要用到HashMap,HashSet等Java集合。 若用不到,仅仅重写equals方法也是可以的。
HashSet是基于HashMap实现的,那咱们找一下HashMap的源码看一下:
只摘取了putVal方法中的一些语句:
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//if语句中,先比较hashcode,再调用equals()比较
//由于“&&”具有短路的功能,只要hashcode不同,也无需再调用equals方法
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
咱们回顾一下hashMap,hashMap是通过key的hashCode去寻找index的,那index一样就形成了链表,比如moon,跟noom计算出来的index都可能是2,那么他们就在一个链表上。
去get的时候,它是根据key去hash然后计算出index,找到了index为2,那这个时候你怎么去找到具体的moon或者noom呢?
equals!就出场了,所以如果我们对equals方法进行了重写,建议一定要对hashCode方法重写,以保证相同的对象返回相同的hash值,不同的对象返回不同的hash值。
不然一个链表的对象,你哪里知道你要找的是哪个,到时候发现hashCode都一样,这不是乱套了嘛。