0%

浅谈equals()和hashCode()

开始思考这个问题,要从一道面试题开始,面试官问我,HashMap中存的对象没有重写hashCode()会有什么影响…

equals()

众所周知,equals()是Java中Object的基本方法,用于比较两个对象是否相等。

==和equals()

经常会被问的问题是==和equals()的区别。使用==对对象进行比较时,是使用两个对象的内存地址进行比较,equals()的话,如果没有对它进行重写,其实也是比较对象的内存地址。所以一般需要我们对他根据具体的业务逻辑进行重写。

hashCode()

这个方法很多人都知道,但不一定真的知道,比如我。
hashCode()也是Object的基本方法,用于计算对象的hash值,默认也是返回对象的内存地址。如果问有没有过重写hashCode的场景,我是没有的;但如果问,有没有使用HashMap存过自定义对象,那肯定是有的。但是使用HashMap存自定义对象却不重写hashCode()方法,那便有问题了。

HashMap和hashCode()

首先设定一个场景

1
2
3
4
5
6
7
8
class Key {
public int k;
// 先不对Key的equals和hashCode进行重写
}
...
HashMap<Key, String> map = new HashMap<>();
map.put(new Key(1), "1");
map.get(new Key(1));

假定int k可以唯一的标识一个Key对象(比如是身份证号),那么从业务角度说,map.get是可以get到上一行put的这个对象返回”1”的,因为认为两次new的Key是同一个对象。
但实际上get()返回的结果是null。为什么?
这要从HashMap的put操作开始说起。HashMap put对象时,会先通过hashCode()方法获取对象Hash值,进行计算得到索引位置以后存到对应位置。get对象时,仍然通过hashCode()方法获取hash值计算出索引,进而找到对象。而如果没有重写hashCode(),则默认返回对象的地址,所以以上场景,两次new的Key对象地址肯定不一样,所以get出来会是null。
那重写了hashCode方法,返回k.hashCode(),因为同样的k的Hash值肯定是一样的(Integer的hashCode()是返回自身的值),这个时候能否get到呢?实际上还是不能。因为还需要重写equals()。
上文说到HashMap方法get时通过对象的hash值找到具体位置,由于Java的HashMap实现是使用链表解决冲突,即冲突的对象们会在相应的索引位置以链表形式存储,因此hash值相同的对象可能会有多个,需要对链表元素进行遍历比较,这时候使用的便是equals()方法。由于没有重写equals()所以默认比较对象的地址,自然也不会有匹配的对象。
如果对equals()和hashCode()都进行重写,就能满足我们需要,返回”1”了。

code

equals()与hashCode()

如此说来,equals()与hashCode()都能用于对象之间的比较,那么他们有何区别呢?

  • 从效果(可靠性)上说,使用equals()方法进行比较,如果结果是true的,则两个对象认为是同一个对象(业务逻辑上来说);如果equals()比较是相等的,则hashCode()返回的结果也会是相等的,因为都是相同的数值进行计算嘛。但是如果是两个不同的对象,其hash值计算结果却是有可能相等的(否则也不会有hash冲突这一说了,这取决于计算的方式)。
  • 从性能上来说,由于equals()的比较涉及具体的业务逻辑,更加全面往往复杂一些,效率就比较低;而hashCode()由于只是涉及hash码的计算生成,所以速度自然更快一些

小结

没有对equals()和hashCode()进行重写,默认都是使用对象的地址进行计算。
equals()返回true的两个对象一定相等,hashCode()返回true时equals()不一定返回true,即对象不一定相等,但hashCode()返回false的两个对象一定不相等
由于两个方法效果和性能的不同,当需要对多个对象进行比较时,可以通过先比较hashCode,如果hashCode一样再进行equals()的比较;如果hashCode都不一样,则没有必要再进行下一步比较了。从而节约一些性能。
需要对多个对象进行比较的场景,比如使用HashMap和HashSet进行存储,由于这两个都不允许存储相同的对象,因此存入过程中必定都涉及容器内已有对象的比较,这种时候先比较hashCode的性能优势就比较明显了。

关于hashCode()返回true,equals()返回false的场景,比如两个对象A,B,都拥有属性String a且是相等,则他们的hashCode就是相等的,实际上他们却是两个不同对象,所以equals()一定是false的。


完结 撒花 ฅ>ω<*ฅ