14

为什么要同时重写 equals 和 HashCode?这个经典面试题你会答吗?

 4 years ago
source link: https://mp.weixin.qq.com/s/q9mpejc8qQeO-GocF-A8Cw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

2ymUBzY.gif

写了多年Java的你,如果被问到: equals和hashCode为什么要同时重写呢? 你有信心能完整地回答上来吗?

不得不说这是一个很基础的问题,但又不失为一个经典的面试题。 如果在面试中真碰到这个问题,我想有可能会有很多小伙伴儿们栽在这个问题上,不信你自己来回答下试试?

ieE3iqf.jpg!web

equals和hashCode是object类的重要方法

如果重写了equals不一定但最好重写hashCode

言归正传,关于是否要同时重写equals和hashCode的问题,我的答案是: 如果重写了equals最好重写hashCode,这几乎是默认的规则。

因为Java对于没有重写equals和hashCode时的规定如下:

如果两个对象通过equals方法比较相等,则他们hashCode的返回值一定要相等。但如果两个对象的hashCode值相等,他们通过equals方法比较的返回值则不一定相等。

如果两个对象hashCode的返回值相等,不能判断两个对象是相等的。但如果两个对象的hashCode的返回值不相等,则可以判定两个对象一定不相等。

以上所说的规则可以简单归纳为:

  • 两个对象相等,hashCode一定相等

  • 两个对象不等,hashCode不一定不等

  • hashCode相等,两个对象不一定相等

  • hashCode不等,两个对象一定不等

可以说如果理解了这四句话,你就能明白equals和hashCode要不要同时重写的问题了。 看到这里,你完全明白了吗? 如果你刚要准备点头却又慢慢摇起头来,那么你可能就是面试被刷的那个人了,你还得接着往下看,下面是对以上总结的具体解释。

VBnUZby.jpg!web

equals和hashCode是两个形影不离的好朋友,但彼此又不那么信任

也许对很多常写增删改查的小伙儿伴来说,这两个方法可能一个都没有重写过,更别说去琢磨是否要同时重写了呢? 所以,要想弄明白不同时重写equals和hashCode会发生怎样的后果,我们需要先整明白以下几点:

  1. equals和hashCode有什么作用? 他们是什么关系?

  2. 为什么要重写equals和hashCode?

  3. 为什么重写了equals最好也重写hashCode?

  4. 如何重写equals和hashCode才正确?

前方高能预警,大家坐稳扶好,老司机要发车了~

01.equals和hashCode有什么作用?他们是什么关系?

要了解这两个方法的作用,咱们得往Java的祖坟上刨,那就得看看Object这个根类了,因为Object是这两个方法的最终发源地。

zqMJviv.jpg!web

先来看equals方法的源码:

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

怎么样是不是很简单? 就是这么粗暴,一个==号就解决了。 很明显equals方法就是比较两个对象是否相等,通过其引用地址值来进行比较。

相比较equals方法,hashCode方法就没有那么简单了,其源码如下:

public native int hashCode();

它是一个native方法,可以根据平台自行实现,但它对其实现提出了一定的要求:

  1. 在同样的条件下,同一个对象无论运行多少次,返回的hash值必须一样。

  2. 如果两个对象通过equals方法比较判定相等,则他们通过hashCode方法返回的hash值也必须相等。

  3. 如果两个对象通过equals方法比较判定不相等,则不必保证他们通过hashCode返回的hash值不相等。

因此,这两个方法的作用就是为了对象间的比较,而他们之间的关系都和其方法的规则和约束有关,就像我们上文总结的规则一致,小伙伴们可以再返回上文回味一下,说不定会有新的领悟哦~

EfIN3m6.jpg!web

02.为什么要重写equals和hashCode?

因为这两个方法都跟对象的比较有关,所以如果在程序中要做对象比较,那大概率要重写这两个方法了。 因为equals默认的比较逻辑就是对象的地址进行比较,两个对象内存地址肯定不同,所以无论如何两个对象通过eqals比较肯定返回false。

但在实际编程中,我们经常会遇到去重,或者将对象放到有序集合中,或者将对象存入无重复的集合中,这时如果没有重写equals和hashCode,则无法达到需求。

举个例子,系统中同时存在两个对象,对象A和对象B,其姓名和身份证号一模一样。 此时,在系统内存中是两个对象,但其内容一致分明是一个人同时产生了两条重复信息。 如果使用默认的equals方法比较,则这两个对象永远不相等,永远不能比出来是一条相同的重复信息。 所以,要重写equals和hashCode方法来达到以上需求效果。

MFVzA37.jpg!web

03.为什么重写了equals最好也重写hashCode?

这个问题其实上面的规则已经讲的很明白了,如果你能理解上面两者的约束关系,你基本不会有这个疑问。 但既然提出来了,我就再啰嗦两句。 接着上面的例子讲,如果有很多对象的信息要放到一个HashSet集合中,我们都知道HashSet有去重唯一的特点,此时,若让这些元素去一个个的equals比较,未免太损耗性能。

所以HashSet的实现引入了HashCode,先比较其HashCode值,如果相等再比较equals,这样就大大提高了效率。所以,如果你只重写了equals而没有重写hashCode,在使用集合时可能会出现意想不到的结果哦!

04.如何重写equals和hashCode才正确?

关于这个问题,你不但要理解上文的重写它们的原因,你必须要遵循好上文提到的规则和约束,否则就会出现你不想看到的状况结果。 此外,再总结下equals重写时必须遵守的规范特性,以确保重写的正确性:

  • 自反性: x.equals(x)==true,自己永远equals自己

  • 对称性: 如果x.equals(y)==true,那么y.equals(x)==true,相同的两个对象互相equals比较永远相等

  • 一致性: 在x、y没有修改的情况下,多次比较x.equals(y)的结果一致

  • 传递性: 如果x.equals(y)==true且y.equals(z)==true,则x.equals(z)==true

总结:以上就是对于equals和hashCode方法为什么要同时重写的经典Java面试题的解答,小伙伴儿们掌握了吗?如果有更多见解,欢迎留言讨论!

U3EZFrA.jpg!web

aai2ueE.png!web


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK