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

1,200 阅读3分钟
原文链接: www.jianshu.com

首先,这是 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等等。