为什么重写equals方法要重写hashCode方法

为什么重写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&trade; 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都一样,这不是乱套了嘛。

发布了302 篇原创文章 · 获赞 23 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/hello_cmy/article/details/105183264