一文搞懂 == 、equals和hashCode
面试的时候,经常会被问到==和equals()的区别是什么?以及我们也知道重写equals()时候必须重新hashCode()。这是为什么?既然有了hashCode()方法了,JDK又为什么要提供equals()方法呢?如果在重写equals()时候没有重写hashCode(),在使用HashMap或HashSet的时候可能会出现什么情况?
一文搞懂 == 、equals和hashCode
== 和 equals()的区别是什么?
先来看看 ==
Java中使用==的时候,如果左右两边是基本类型和两边是应用类型的作用效果是不同的:
我们看看下面如下代码:
int x = 128;
int y = 128;
Person p = new Person(new Address("北京"));
Person p2 = p.clone();
System.out.println("两个基本类型==后值:");
System.out.println(x==y);
System.out.println("两个对象(引用类型)==后值:");
System.out.println(p == p2);
System.out.println(" \n p的地址值为:"+p +" \n p2的地址值为:"+p2.toString());
输出的结果是什么?

从上面结果,我们可以得到如下结论:
当 == 左右两边是基本类型的时候,其实就是比较的是数值是否相等;
当 == 左右两边是对象(引用)类型的时候,其实比较的是p和p2这两个对象所指向的堆中的对象地址,一般我们简称:比较的是内存地址值。
需要注意:
因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
来看看equals()
equals()方法特点:
1:equals()方法不能用于判断基本类型的变量,只能用来判断两个对象是否相等。
2:equals()方法存在于Object类中的。而我们又指导Object类是所有类的直接或者间接的父类。所以所有类都具有equals()方法
看看Object源码中equals()方法:

从源码中我们可以看出,底层其实使用的是 == 。
== 左右两边都是对象。从上面我们知道==比较对象,其实就是比较对象内存中的地址值。
所以,我们可以得到equals()方法存在两种使用情况的结论:
1:类没有重写equals()方法:
当两个对象没有重写equals()方法时候,通过equals()方法进行比较的时候,其实就等价于通过"=="比较两个对象。因为在没有重新equals方法的情况下默认都使用的是Object类的equals()方法;
2:类重写了equals()方法:
一般在工作中,我们都重写equals()方法来比较两个对象中的属性是否相等。如果两个对象的属性相等,则返回true.就认为两个对象是相等的。
代码如下:
定义一个Girl对象,有两个属性:样貌和肤色。然后重写equals()方法

测试重写了equals()方法后,两个girl通过equals比较:

我们来看看输出的结果:

equal()方法输入的是:true
但是实际上,两个Girl对象在堆中的内存地址值不一样。
我们在Girl对象中添加地址对象属性,在重写equals方法:

测试:
结果:

从测试效果来看,可以验证结论:equals()比较两个重新equals()方法对象的时候,其实就是比较的是两个对象中每个属性值。
现在再来回答 == 和 equals()方法有什么区别?这个问题应该好回答了吧。
接下来,我们在来看看hashCode()方法
hashcCode是什么?
我们在调用对象的hashCode()方法的时候,返回的是一个int整数。这个整数其实是散列码,不过我们习惯称之为哈希码。作用就是确定这个对象在hash表中的所以位置。
出处:
hashCode()方法被定义在Object类中。这也就意味着任何一个类都有hashCode()这个方法(和equals()方法一样,都是被定义在Object对象中)。查看Object的源码,我们可以发现,次方法被native关键字修饰的。也就是说,Object中的hashCode()方法调用的是本地方法的。其实就是调用操作系统自己的hashCode()方法(用C语言或者是C++语言实现的)。该方法通常用来将对象的内存地址转换成整数后返回的。

那么为什么要有hashCode?
起始hash存储的是键值对(K-V)形式的,其特点就是:能够根据"key"快速的检索出对应的"值"。在快速检索的时候,就使用到了哈希码。
回想下hashMap在put对象的时候,先计算出key对应的hashCode值,来判断对象需要加入的位置。如果不存在,就直接插入,如果存在,就加到链表中。如下图:

从上面我们可以知道,起始 hashCode()和equals()这两个方法都是用于比较两个对象是否相等的。
问题:既然两个方法都是比较对象是否相等,那么为什么JDK还要同时提供这两个方法呢?
答:为了提高效率。
还以hashMap的put方法为例,我们知道,先计算出hashCode,如果不存在,就可以直接put了。不用比较了,少了一次比较。效率就高了。
问题:那么能否只使用hashCode()方法呢?
答:不能。因为我们知道,哈希码是通过函数算出来的整数。既然使用的是公式,那么可能出现两个对象不一样,但是哈希码一样的。
就比如我们使用 a+b这个公式得出的一个整数一样。4+4 = 8;5+3=8;
经过公式计算的结果都是8,但是两个算式的a和b却是不相等的。
问题:如果两个对象的hashCode值相等,它们相等吗?
答:不相等。如:4+4 = 8;5+3=8;
通过上面说明,我们可以得到hashcode相关结论:
1:两个对象hashcode想的,那么这两个对象不一样相等(hash碰撞了。如:4+4 = 8;5+3=8;)
2:如果两个对象的hashCode值不相等,那么这两个对象就不相等
通过上面我们分析equals()方法,我们还可以得到下面这个结论:
3:如果两个对象的hashCode想的呢并且equals()方法返回的也是true。那么我们才能认为这两个对象相等的。
因为:4+4 = 8;4+4 = 8; 其中的8就是hashCode. 两个算式的 a、b都是4,也是相等的。
问题:为什么重写equals()时候必须重写hashCode()方法?
因为一般在重写equals()方法的时候,是要对两个对象进行比较的。如果两个对象相等的话,hashCode值必须相等,equals()方法判断两个对象也是相等的。
如果重写equals()方法时候,没有重写hashCode()方法的话,可能导致equals()方法判断想的的两个对象hashCCode值却不相等。如下示例:

我们来看看结果:

总结:
重写equals()方法是好,必须要重写hashCode()方法。
思考:重写equals()方法时候,没有重写hashCode()方法的haul,在使用HashMap/HashSet时候可能会出现什么问题?
我们以hashSet为例(hashSet底层使用的是hashMap来实现的):

结果:

(꒪ꇴ꒪(꒪ꇴ꒪ ;)哈? 不是说hashSet是唯一的,不能有重复的吗?打印出来的set集合大小是2啊,不是1啊。
其实,这就是只重写了equals(),没有重写hashCode()方法的后果。
因为在set.add()方法时候,先判断hashcode值,从上图我们可以看到,两个对象hashCode值不相等。set就认为不是一个对象,所以大小就是2了。
so,我们在重写equals()方法的时候,一定要重写hashCode()方法
一文搞懂 == 、equals和hashCode的更多相关文章
- 一文搞懂所有Java集合面试题
Java集合 刚刚经历过秋招,看了大量的面经,顺便将常见的Java集合常考知识点总结了一下,并根据被问到的频率大致做了一个标注.一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多.两颗 ...
- 一文搞懂RAM、ROM、SDRAM、DRAM、DDR、flash等存储介质
一文搞懂RAM.ROM.SDRAM.DRAM.DDR.flash等存储介质 存储介质基本分类:ROM和RAM RAM:随机访问存储器(Random Access Memory),易失性.是与CPU直接 ...
- 基础篇|一文搞懂RNN(循环神经网络)
基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...
- 一文搞懂 Prometheus 的直方图
原文链接:一文搞懂 Prometheus 的直方图 Prometheus 中提供了四种指标类型(参考:Prometheus 的指标类型),其中直方图(Histogram)和摘要(Summary)是最复 ...
- Web端即时通讯基础知识补课:一文搞懂跨域的所有问题!
本文原作者: Wizey,作者博客:http://wenshixin.gitee.io,即时通讯网收录时有改动,感谢原作者的无私分享. 1.引言 典型的Web端即时通讯技术应用场景,主要有以下两种形式 ...
- 一文搞懂vim复制粘贴
转载自本人独立博客https://liushiming.cn/2020/01/18/copy-and-paste-in-vim/ 概述 复制粘贴是文本编辑最常用的功能,但是在vim中复制粘贴还是有点麻 ...
- 三文搞懂学会Docker容器技术(中)
接着上面一篇:三文搞懂学会Docker容器技术(上) 6,Docker容器 6.1 创建并启动容器 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] --na ...
- 三文搞懂学会Docker容器技术(下)
接着上面一篇:三文搞懂学会Docker容器技术(上) 三文搞懂学会Docker容器技术(中) 7,Docker容器目录挂载 7.1 简介 容器目录挂载: 我们可以在创建容器的时候,将宿主机的目录与容器 ...
- 一文搞懂 js 中的各种 for 循环的不同之处
一文搞懂 js 中的各种 for 循环的不同之处 See the Pen for...in vs for...of by xgqfrms (@xgqfrms) on CodePen. for &quo ...
- 一文搞懂如何使用Node.js进行TCP网络通信
摘要: 网络是通信互联的基础,Node.js提供了net.http.dgram等模块,分别用来实现TCP.HTTP.UDP的通信,本文主要对使用Node.js的TCP通信部份进行实践记录. 本文分享自 ...
随机推荐
- TI AM62x工业核心板规格书(单/双/四核ARM Cortex-A53 + 单核ARM Cortex-M4F,主频1.4GHz)
1 核心板简介 创龙科技SOM-TL62x是一款基于TI Sitara系列AM62x单/双/四核ARM Cortex-A53 + 单核ARM Cortex-M4F异构多核处理器设计的高性能低功耗工业级 ...
- Mysql通过frm和ibd恢复数据库
昨天的考试过程中,有个考点的服务器蓝屏重启后发现Mysql启动不了(5.6.45 x32版本,使用innoDB),重装后无法加载原数据库记录,通过查询资料,通过frm和idb文件成功恢复了数据库记录. ...
- SpringBoot快速插入Mysql 1000万条数据
导读 有时候为了验证系统瓶颈,需要往数据库表中插入大量数据,可以写sheel脚本插入,前几天为了插入100万条数据,走的sheel脚本(点我直达),插入速度简直无法直视,花了3小时,才插入了10万条, ...
- 解决方案 | pyautogui实现等待特定图片(对话框)出现(可设置等待超时时间)
1.问题 为了等待某个[转换完毕]的图片(对话框)出现,如何设置? 2.解决方案代码如下 下面代码实现的是:设置超时时间为10s,当你在完成前序工作以后,代码开始进入等待,一旦你的特定图片出现了,马上 ...
- 假期小结7爬虫学习requests
这周我初步学习了py爬虫的相关知识,以下是我的部分总结 URL headers(URL头部)是HTTP请求中包含的一部分信息,用于描述.控制和传递请求的各种元数据.它们是位于HTTP请求消息的起始部分 ...
- 【Vue2】金额范围查询项
Element 只提供了DatePicker,没有做金额的Picker 这个东西就只能自己做了,实现效果: 后台接口条件: 因为有可能只有起始值,只有结束值,或者起始值结束值都有三种情况 1.如果有起 ...
- 【转载】 gym atari游戏的环境设置问题:Breakout-v0, Breakout-v4, BreakoutNoFrameskip-v4和BreakoutDeterministic-v4的区别
版权声明:本文为CSDN博主「ok_kakaka」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/clksjx/ ...
- 【转载】 Makefile的静态模式%.o : %.c
版权声明:本文为CSDN博主「猪哥-嵌入式」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/u012351051 ...
- vue之父组件与子组件的通信
1.背景 参数传递(多理解) 1.父传子<c-parent :messagevue="message"></c-parent>,请看下面具体的截图描述 2. ...
- maven配置阿里云镜像与修改默认仓库地址
1.背景 通常来说maven的默认镜像很慢,我们需要一个国内镜像,拉取jar包的时候从国内下载, 当然阿里云镜像是很好的一个候选 2.安装 官网下载一个maven解压即可使用 3.修改配置 第一步,找 ...