史上最全的 Java 容器集合之 equals 和 hashCode
前言
文本已收录至我的 GitHub 仓库,欢迎 Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是现在
絮叨
List 集合讲完了,接下来要讲 Map,因为 Set 底层是 Map。但是讲 Map 之前得好好讲讲 equals 和 hashCode
🔥史上最全的Java容器集合之ArrayList(源码解读)
🔥史上最全的Java容器集合之Vector和LinkedList(源码解读)
为什么要讲这 2 个方法呢?因为 Listd 存的数据可以找的到,但是 Map 内部存储元素的方式的都是以键值对相关的,而元素如何存放,便与 equals 和 hashCode 这两个方法密切相关,所以分析 Map 之前,我们来把 equals 和 hashCode 好好的探究一下,也为了以后更好的理解 Map
equals()方法
Object 原生的 equals 方法:
从代码中可以看出,原生的 equals 方法使用的是“==”来比较的。学过 Java 的人都应该知道,“==”比较的是内存地址,所以原生的 equals 方法只有在自己与自己比较的时候才会返回 true,是严格的判断一个对象是否相等的方法。所以,如果类具有自己特有的“逻辑相等”概念(不同于对象相等的概念),而且超类没有覆盖 equals()方法以实现期望的行为,这时我们就需要覆盖 equals()方法了(通俗来说,就是在业务系统中,有时候需要的并不是一种严格意义上的相等,而是业务上的对象相等。比如:如果两个对象中的 id 相等,那么就认为这两个对象是相等的),这时候我们就需要对 equals 方法进行重写,定义新的比较方式。
覆盖 equals 方法时,需要遵守的通用约定覆盖 equals 方法时,需要遵守的通用约定
自省性:对于非 null 的 x,存在:x.equals(x)返回 true
对称性:对于非 null 的 x 和 y,存在:x.equals(y)==y.equals(x)
传递性:对于非 null 的 x、y、z,存在:当 x.equals(y)返回 true,y.equals(z)返回 true,则 x.equals(z)一定为 true
一致性:对于非 null 的 x 和 y,多次调用 x.equals(y)所得的结果是不变的
非空性:对于非 null 的 x,存在 x.equals(null)返回 false
String 里面重写的 equals 方法
很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推 Math、Integer、Double 等这些类都是重写了 equals()方法的,从而进行的是内容的比较。当然,基本类型是进行值的比较。
hashCode()方法
String 中的 HashCode 方法
他就是用 31*上一个字符的数值+当前字符的大小,然后依次遍历得到 String 类型的 hash 值
在 Object 中 hashCode 是一个 Native 方法。hashCode 一般用于计算对象的 hash 值,它在类重写 equals 的时候一起重写,重写它的目的是为了保证 equals 相同的两个对象的 hashCode 结果一致,为什么要保证这一点呢,那就归结到 java 中的那几个基于 Hash 实现的集合上了,比如 HashMap、HashSet 等,这些集合需要用到对象的 hash 值来参与计算定位。使用 hashCode 的目的就是为了散列元素,最终元素能否散列均匀和 hashCode 的实现息息相关,即为 hash 函数。
hashCode 的作用
想要弄明白 hashCode 的作用,就要回到我们所说的容器中来了,Java 中的集合(Collection)有两类,一类是 List,再有一类是 Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
这就是 Object.equals 方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有 1000 个元素,那么第 1001 个元素加入集合时,它就要调用 1000 次 equals 方法。这显然会大大降低效率。
于是,Java 采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,初学者可以简单理解,hashCode 方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用 equals 方法的次数就大大降低了,几乎只需要一两次。
结论
eqauls 方法和 hashCode 方法是这样规定的
如果两个对象相同,那么它们的 hashCode 值一定要相同;
如果两个对象的 hashCode 相同,它们并不一定相同(这里说的对象相同指的是用 eqauls 方法比较)。
equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的 hashcode()不相等。
为什么要重写 equals 呢?因为在 java 的集合框架中,是通过 equals 来判断两个对象是否相等的
版本说明
这里的源码是 JDK8 版本,不同版本可能会有所差异,但是基本原理都是一样的。
结尾
好了 hashCode 和 equals 讲完了,我们就可以知道如何来保证我们存的数据不重复了,接下开大头戏 HashMap 开始了,我想有了这个基础,会好理解很多。
因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。
日常求赞
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。
创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见
六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !
版权声明: 本文为 InfoQ 作者【自然】的原创文章。
原文链接:【http://xie.infoq.cn/article/bd0e8de18a4ac3aa4a8859a8d】。文章转载请联系作者。
评论