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

首先,这是 Java 规范。为什么要有这样的规范呢?还得从 hash 原理说起。

举个例子。现在有 1000 个字符串,都是人名,比如 Jack、Tom 等。最简单的存储方式是,将这 1000 个字符串存入一个数组里。假如 Jack 存在于 311 这个位置。如果我现在要找到它,必须得和 数组里的的数据逐个比较,从下标为1的位置开始比较,然后和下标为2的位置比较,直到比较到 311 这个位置的数据。

那么,有没有更高效的方式呢?有。很多。hash 就是最典型的一种。说 hash 之前,要提到数组的一个特点了。在一个数组里,假如我知道某个数据(比如Jack)存放的位置(下标 311),我就可以一步就取出这个数据(Jack)。试想,将 Jack 和数组下标 311 通过某种方式关联起来。那么查找效率是不是会变得高效?我们先尝试着关联一下看看。

比如,如果将 a-z 和 1-26 这些数字映射起来。那么Jack就是 J=10,a=1,c=3,k=11.那么 Jack = 101311 和 1000 取模为 311。那么我通过这种方式,将 Jack 存在下标为 311 的单元格中。等到取数据的时候,比如有个场景,查询数组里有没有 Jack 这个字符串。那么我可以用 Jack 经过同样的计算方式,得到一个值为 311 。然后在数组里,获取到下标为 311 这个单元格里面的数据。这样,查找效率就比一个一个顺着往下比较高很多倍。

hash桶实际是一个数组

hash碰撞的缘故,可能一个单元格里存有多个数据,比如 311这个格子里不仅有Jack, 还有 Tom ,所以需要 equals 来进行字符串匹配了。

这里,可以看出 hashCode 和 equals 方法分别做了什么。某个数据,通过算法,得到 hashCode,然后将这个数据存入下标等于 hashCode 的单元格里。这样,就将数据和数组下标关联起来了。因为hash碰撞,一个单元格可能存有多个数据, equals 就是为了比较一个单元格里是否有目标数据。

所以,重写 equals 需要重写 hashCode。

总之,hash算法是利用数组寻下标访问速度高效的特点。将存储的元素和数组下标关联起来。来达到高查找效率的目标。

Java 中,当用到HashMap,HashSet这类的集合框架时,比如,将自己新建的类作为 HashMap 的 key。这种情况下,需要重写hashCode和equals方法。重写的情景很少,但是掌握了hash原理,也就能搞明白数据库的hash索引、一致性hash等等。

hash算法的优点:查找效率高
hash算法的缺点:无法进行部分匹配查询。比如key是jack,我想找key里包含“jac”对应的value。没有顺序,无法在hash里面进行排序。扩容的时候,需要重新hash,效率会比较低。

备注:Object 的 hashcode 不一定是对象地址引用,要看实际 JVM。== 比较的是地址引用,而 Oject 的 equals 是封装 ==

推荐阅读更多精彩内容