面试的时候,经常会被问到==和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的更多相关文章

  1. 一文搞懂所有Java集合面试题

    Java集合 刚刚经历过秋招,看了大量的面经,顺便将常见的Java集合常考知识点总结了一下,并根据被问到的频率大致做了一个标注.一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多.两颗 ...

  2. 一文搞懂RAM、ROM、SDRAM、DRAM、DDR、flash等存储介质

    一文搞懂RAM.ROM.SDRAM.DRAM.DDR.flash等存储介质 存储介质基本分类:ROM和RAM RAM:随机访问存储器(Random Access Memory),易失性.是与CPU直接 ...

  3. 基础篇|一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  4. 一文搞懂 Prometheus 的直方图

    原文链接:一文搞懂 Prometheus 的直方图 Prometheus 中提供了四种指标类型(参考:Prometheus 的指标类型),其中直方图(Histogram)和摘要(Summary)是最复 ...

  5. Web端即时通讯基础知识补课:一文搞懂跨域的所有问题!

    本文原作者: Wizey,作者博客:http://wenshixin.gitee.io,即时通讯网收录时有改动,感谢原作者的无私分享. 1.引言 典型的Web端即时通讯技术应用场景,主要有以下两种形式 ...

  6. 一文搞懂vim复制粘贴

    转载自本人独立博客https://liushiming.cn/2020/01/18/copy-and-paste-in-vim/ 概述 复制粘贴是文本编辑最常用的功能,但是在vim中复制粘贴还是有点麻 ...

  7. 三文搞懂学会Docker容器技术(中)

    接着上面一篇:三文搞懂学会Docker容器技术(上) 6,Docker容器 6.1 创建并启动容器 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] --na ...

  8. 三文搞懂学会Docker容器技术(下)

    接着上面一篇:三文搞懂学会Docker容器技术(上) 三文搞懂学会Docker容器技术(中) 7,Docker容器目录挂载 7.1 简介 容器目录挂载: 我们可以在创建容器的时候,将宿主机的目录与容器 ...

  9. 一文搞懂 js 中的各种 for 循环的不同之处

    一文搞懂 js 中的各种 for 循环的不同之处 See the Pen for...in vs for...of by xgqfrms (@xgqfrms) on CodePen. for &quo ...

  10. 一文搞懂如何使用Node.js进行TCP网络通信

    摘要: 网络是通信互联的基础,Node.js提供了net.http.dgram等模块,分别用来实现TCP.HTTP.UDP的通信,本文主要对使用Node.js的TCP通信部份进行实践记录. 本文分享自 ...

随机推荐

  1. 继续我们的复习之路——webapi

    前面断更几天是因为在住安心复习DOM BOM的内容 不得不说 还得是DOM 在这一章节的复习内容中  涌现出了很多又代表意义 经典的一些小案例 而且 还是有些难度的 有一两个我反正是自己独立完成不了 ...

  2. Spring Reactor基本介绍和案例

    1. Reactor 对比 1.1 Reactor 线程模型 Reactor 线程模型就是通过 单个线程 使用 Java NIO 包中的 Selector 的 select()方法,进行监听.当获取到 ...

  3. 手把手教你解决spring boot导入swagger2版本冲突问题,刘老师教编程

    手把手教你解决spring boot导入swagger2版本冲突问题 本文仅为个人理解,欢迎大家批评指错 首先Spring Boot 3 和 Swagger 2 不兼容.在 Spring Boot 3 ...

  4. n阶前缀和 の 拆解

    二阶 \[\sum_{i=l}^{r} \sum^{i}_{j=1} a_j \] \[=\sum_{i=l}^{r} (r-i+1) a_i \] \[=(r+1)\sum_{i=l}^{r} a_ ...

  5. 创业型公司和BAT大厂招聘要求有什么不同?

    背景 很多Java初学都关心这么一个问题,Java学到什么程度以后可以找到第一份工作.大家的目标都很明确,也很实在,学习Java无非就是为了找工作,那到底我要学多少Java知识,才可以找到第一份工作呢 ...

  6. 2024秋招字节跳动朝夕光年UE4客户端开发实习生岗笔试题目

    20240117更新 2024年秋招笔试题目,没想到时隔几个月字节跳动游戏业务就要寄了,本文仅供参考,请大佬多多指教 Q1字符串处理 Q2 杯子问题 桌子上有4109+1个饮料杯,这些饮料杯的编号依次 ...

  7. iframe嵌入svg图片动态更改文本样式并进行缩放等功能实现拓扑图

    好久没更了,近期开发遇到的需求,抽空梳理一下~ 需求:实现一个复杂的拓扑图,图中元素的个数,以及各个参数内容是动态展示的. 于是让ui提供了对应的svg图片. 解决思路:使用iframe嵌入svg图片 ...

  8. 前缀函数及 Knuth–Morris–Pratt 算法学习笔记

    \(\text{1 引言 Preface}\) 对于形如以下的问题: 给予一个模式串 \(T\) 和主串 \(S\),在主串中寻找 \(T\). 我们称之为字符串匹配. 很显然朴素算法时间复杂度是 \ ...

  9. appium模拟键盘事件

    原方法: 1 def press_keycode(self, keycode, metastate=None): 2 """Sends a keycode to the ...

  10. python __del__() 清空对象

    python __del__() 清空对象 python垃圾回收机制:当一个对象的引用被完全清空之后,就会调用__del__()方法来清空这个对象 当对象的引用没有被完全清空时,代码如下: class ...