Loading [MathJax]/jax/output/PreviewHTML/jax.js

关于equals和hashCode方法

本篇文章记录学习方法和hashCode()方法的内容。

什么是hashCode

通常所说的hashCode是一个经过哈希运算后的整型值,这个哈希运算的方法,定义在Object类中,通过一个本地方法hashCode()来实现(在hashMap中还会有一些其他的运算)。

1
public native int hashCode();

要了解这个方法到底是什么作用,最有效的方法就是直接看源码注释。

image-20211203203212634

image-20211203203212634

返回当前对象的一个哈希值。这个方法用于支持一些哈希表,例如hashMap

通常来讲,它有一下一些约定:

  • 若对象的信息没有被修改,那么,在一个程序的执行期间,对于相同的对象,不管调用多少次hashCode方法,都应该返回相同的值。当然,在相同程序的不同执行期间,不需要保持结果的一致。
  • 若两个对象的equals()方法返回值相同,那么,他们调用各自的hashCode方法时,也必须返回相同的结果
  • 当两个对象的equals()方法返回值不同时,那么他们的hashCode方法不用保证必须返回不同的值。但是,我们应该知道,在这种情况下,最好也设计成hashCode返回不同的值。因为,这样做有助于提高哈希表的性能。

实际情况下,Object类的hashCode方法在不同的对象确实返回了不同的哈希值。这通常是通过把对象的内部地址转换成一个整数来实现的。

这里所说的内部地址就是物理地址。需要注意的是,虽然hashCode值是依据它的物理地址而得来的。但是,不能说hashCode就代表对象的内存地址。

hashCode有什么用

在哈希表中,通过key计算出它的hashCode​值,再进行处理就可以确定它在哈希表中的位置,这样,在查询时,就可以直接定位到当前元素,提高查询效率。对于要插入的一个新元素,先去计算它的hashCode​值,如果此位置没有元素,那么就直接插入即可。如果此位置已经有值,可以通过equals()方法比较它们是否相等,不等则也插入到这个位置(可以用链表形式存放)。所以,hashCode提高了查询,插入元素的效率。

equals==的区别

equals比较的是内容,而 == 比较的是地址。

equals()方法是定义在Object类中的。

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

可以看到,它的默认实现,就是 == ,用来比较两个对象的内存地址是否相等。如果一个对象不重写equals​,那么效果和 == 是一样的。

因此,在使用自定义类的对象时,如果要让两个对象的内容相同时认为对象时相同的,则需要重写equals方法。

为什么要重写equalshashCode

前面已经说明为什么重写equals,重写hashCode就涉及到MapSet(底层其实也是Map)的内容了。

hashMap jdk1.8的源码来看,如put方法。

image-20211203210055456

image-20211203210055456

可以看到,在插入元素时,代码中多次进行hash​值的比较,只有当hash​值相等时,才会去比较equals()方法。当哈希值和equals比较都相等时,才会覆盖元素。get​方法也是如此。

只有当hashCodeequals都相等时,才认为是同一个元素。

重写hashCodeequals的目的,就是为了方便哈希表这样的结构快速的查询和插入,不重写,则无法比较元素,可能造成元素位置错乱。

重写了equals,就必须重写hashCode

JDK​源码注释第二点就说明了这一点。如果不重写hashCode​,对于我们自定义的类,创建的两个内容相同的对象,将其中一个对象加入到hashmap中,另一个对象调用其hashCode查找位置仍能插入成功,这是不正确的。

还需要注意的是,讲对象插入到了hashMap后,不要在使用过程中,改变对象的值,这样会导致hashCode值发生改变,无法再获取到插入的值。

String类具有不可变性,所以我们经常使用String类作为hashMapkey​值。

hashCode相等,equals一定相等吗

显然不是。在源码中,当通过hashCode值处理后计算出来的位置相等(产生哈希碰撞)时,还需要比较它们的equals​,才能确定是否是同一个对象。因此,hashCode​相等时,equals不一定相等。

反过来,equals相等,那么hashCode一定相等吗?是一定的。equals都相等了,那么在hashMap中认为它们是同一个元素,那么hashCode值必须保证相等。

总结

hashCode相等,equals不一定相等

hashCode不等,equals一定不等

equals相等,hashCode一定相等

equals不等,hashCode不一定不等

在源码注释第三点也提到,当equals不等时。不必保证它们的hashCode不等。但是为了提高哈希表的效率,最好设计成不等。因为,我们既然知道它们不相等了,那么当 hashCode 设计成不等时。只要比较hashCode 不相等,我们就可以直接返回 null,而不必再去比较 equals了。这样,就减少了比较的次数,无疑提高了效率。

Author

叶润繁

Posted on

2021-11-25

Licensed under