深入理解equals和hashCode关系和区别
直入主题:
很多人把他们放在一起比较,那我们首先要想到的是,他们肯定有大致相同的作用,和一些细小的区别。
先说说他们相同的作用:equals和hashCode方法都是用来判断两个对象的值是否相等,请记住这里说的相等仅仅说的是两个对象的值,和他们的引用无关
区别:
1.他们判断对象相同的方式不一样:
2.他们判断对象是否相等的准确率不一样:
为啥这样说,因为hashCode有极小概率会判断错,而equals的判断结果是百分百正确的
相信看到这里已经有朋友有疑问了,既然hashCode方法不能准确判断,那我们为什么还要用它?哈哈哈,因为我们无法忍受丢弃他的另一个优点,就是效率高,接着往下看,我们慢慢捋一捋。
先说上面的第一点:判断对象的相等的方式不一样
1.equals:重写的equals方法,比如String 的equals方法,如图:
他最终的目的循环判断两个对象的每一个字符是否相等,所有字符全部对应相等,那他们的值肯定也就相等了,这是equals的判断方法
hashCode的判断方法:hashCode是通过用hash算法来计算具体对象实例,得到斌返回一个hashcode值。通过比较hashcode值是否相等来判断两个对象是否相等,
相信大家看到这里是已经有点懵逼了,别急,接下来具体讲讲他的原理:
以java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个Hash哈希表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的hashcode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。具体过程是这样:
- new Object(),JVM根据这个对象的Hashcode值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。
- 比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal.
可能经过上面理论的讲一下大家都迷糊了,我也看了之后也是似懂非懂的。下面我举个例子详细说明下。
list是可以重复的,set是不可以重复的。那么set存储数据的时候是怎样判断存进的数据是否已经存在。使用equals()方法呢,还是hashcode()方法。
假如用equals(),那么存储一个元素就要跟已存在的所有元素比较一遍,比如已存入100个元素,那么存101个元素的时候,就要调用equals方法100次。
但如果用hashcode()方法的话,他就利用了hash算法来存储数据的。
这样的话每存一个数据就调用一次hashcode()方法,得到一个hashcode值及存入的位置。如果该位置不存在数据那么就直接存入,否则调用一次equals()方法,不相同则存,相同不存。这样下来整个存储下来不需要调用几次equals方法,虽然多了几次hashcode方法,但相对于前面来讲效率高了不少。
上面开始的时候我着重说了重写的equals方法,为什么要重写呢?
因为Object的equal方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等;如果你现在需要利用对象里面的值来判断是否相等,则重载equal方法。
说道这个地方我相信很多人会有疑问,相信大家都被String对象的equals()方法和"= ="纠结过一段时间,当时我们知道String对象中equals方法是判断值的,而= =是地址判断。
那照这么说equals怎么会是地址的比较呢?
那是因为实际上JDK中,String、Math等封装类都对Object中的equals()方法进行了重写。
我们先看看Object中equals方法的源码:
我们都知道所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较两个对象的的内存地址,所以说使用Object的equals()方法是比较两个对象的内存地址是否相等,即若object1.equals(object2)为true,则表示equals1和equals2实际上是引用同一个对象。虽然有时候Object的equals()方法可以满足我们一些基本的要求,但是我们必须要清楚我们很大部分时间都是进行两个对象的比较,这个时候Object的equals()方法就不可以了,所以才会有String这些类对equals方法的改写,依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的,从而进行的是内容的比较。希望大家不要搞混了。
改写equals时总是要改写hashcode
java.lnag.Object中对hashCode的约定:
- 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
- 如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
- 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。
根据上一个问题,实际上我们已经能很简单的解释这一点了,比如改写String中的equals为基于内容上的比较而不是内存地址的话,那么虽然equals相等,但并不代表内存地址相等,由hashcode方法的定义可知内存地址不同,没改写的hashcode值也可能不同。所以违背了第二条约定。
又如new一个对象,再new一个内容相等的对象,调用equals方法返回的true,但他们的hashcode值不同,将两个对象存入HashSet中,会使得其中包含两个相等的对象,因为是先检索hashcode值,不等的情况下才会去比较equals方法的。 一定要看的干货
深入理解equals和hashCode关系和区别的更多相关文章
- 帮助新手理解equals和hashCode
入行快要两年,偶尔想起来equals和hash还是会有些晕,索性今天就更深入的弄明白一些,不足之处也请各位大神指出批评,共同进步. 刚开始学java的时候只是记忆性的来背,如果一个类在程序中可能进行比 ...
- JAVA中用堆和栈的概念来理解equals() "=="和hashcode()
在学习java基本数据类型和复杂数据类型的时候,特别是equals()"=="和hashcode()部分时,不是很懂,也停留了很长时间,最后终于有点眉目了. 要理解equals() ...
- java ==、equals、hashcode有什么区别
1.== 用来比较两个对象的存储空间 2.equals是Object类提供的方法之一,每个java类都继承Object类,所以每一个对象都具有equals方法,所以在没有覆盖equals方法的情况下, ...
- 大杂烩 -- equals、hashCode联系与区别
基础大杂烩 -- 目录 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Equals 1.默认情况(没有覆盖equals方 ...
- "=="、equals、hashCode之间的区别
1. "=="分为两种情况: (1) 基本数据类型,比较的是其对应的值是否相等: (2) 引用类型,比较的是他们在内存中存放的地址(或者说,是否指向同意对象). 2. equals ...
- Java中equals,hashcode,==的区别
== :比较java栈局部变量表中变量的地址或值是否相等. equals : 比较变量的地址在java堆中引用对象是否为同一个对象. hashcode : 通过对象在JVM内存中的存储地址通过特定算 ...
- java中equals,hashcode和==的区别
https://www.cnblogs.com/kexianting/p/8508207.html
- ==、equals()、hashcode()的关系和区别
==.equals().hashcode()概念 ==:它的作用是判断两个对象的地址是不是相等.即,判断两个对象是不试同一个对象. equals():它的作用也是判断两个对象是否相等.但它一般有两种使 ...
- Java == ,equals 和 hashcode 的区别和联系(阿里面试)
今天阿里的人问我 equals 与hashcode的区别,我答不上来, 仔细查了一下,做了总结: (1) == 这是Java 比较内存地址,就是内存中的对象: java中的==是比较两个对象在JVM中 ...
随机推荐
- html5 cavans的小应用
1.canvas鼠标画线,canvas小方块移动,canvas小方块旋转并缩放 <!doctype html> <html> <head> <meta cha ...
- 三、create-react-app新旧版中使用less和antd并修改主题颜色
引入less 如果项目根目录中没有config文件夹,首先暴露出项目配置文件,项目下执行: npm run eject 如果项目是从git仓库中pull下来的的话,必须确保本地项目与仓库中没有冲突,才 ...
- 【Weiss】【第03章】链表例程的一些修改
主要是,感觉原来的链表例程通过Node的分配形成了链表,但是没有自动消除Node的办法比较危险,一旦在clear()之前把链表赋了其它值就内存泄漏了. 所以改了析构函数,自动清理分配出来的内存.既然改 ...
- 面试官:说说你对css效率的理解
大家好,我是小雨小雨,致力于分享有趣的.实用的技术文章. 内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步. 大家的支持是我创作的动力. 选择器的优先级 众所周知,选择器是有权重 ...
- Volatile关键字回顾之线程可见性
java中,volatile关键字有两大作用: 1.保证线程的可见性 2.防止指令重排序 这篇文章主要通过典型案例,体现可见性这一特性. 概念: java中,堆内存是线程共享的.而每个线程,都应该有自 ...
- Salesforce LWC学习(十四) Continuation进行异步callout获取数据
本篇参考: https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.apex_continua ...
- 从字节码来分析,i++与++i区别
++/-- 是一种特殊的算术运算符,在算术运算符中需要两个操作数来进行运算,而自增自减运算符是一个操作数 前缀自增(++a):先进行自增运算,再进行表达式运算: 后缀自增(a++):先进行表达式运算, ...
- fastText 训练和使用
2019-09-09 16:33:11 问题描述:fastText是如何进行文本分类的. 问题求解: fastText是一种Facebook AI Research在16年开源的一个文本分类器. 其特 ...
- Anaconda常用命令收藏----后期还会更新
一.更换jupyter notobook的打开目录 一般载在安装好Anaconda的时候,打开jupyter指向的目录一般是系统的根目录,如C:\Users\25282,但是这样的话对C盘是个不小的负 ...
- [JVM教程与调优] 什么是JVM运行时参数?
我们接着上一章节[JVM教程与调优] JVM都有哪些参数类型?的内容继续讲解,这章我们来介绍一下:如何查看JVM运行时参数.这一点十分重要,因为我们在进行JVM参数调优的时候,我们首先得知道目前系统运 ...