Object类 equals方法 hashCode方法 集合

Object类

Object类是所有类的父类,所以:

  1. Object的类的成员变量和成员方法,其余的类会继承,可以使用在这里插入图片描述

  2. Object类可以使用多态创建任意对象,同时拥有子类的重写方法

在这里插入图片描述
我们先假设子类重写了equals方法和hashCode方法(IDEA默认的重写)

public class Human {private String name;private int age;public void setName(String name) {this.name = name;}public String getName() {return name;}public void setAge(int age) {this.age = age;}public int getAge() {return age;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (!(o instanceof Human)) return false;Human human = (Human) o;if (getAge() != human.getAge()) return false;return getName() != null ? getName().equals(human.getName()) : human.getName() == null;}@Overridepublic int hashCode() {int result = getName() != null ? getName().hashCode() : 0;result = 31 * result + getAge();return result;}
}

此时我们调用equals方法

public class ObjectTest {public static void main(String[] args) throws Throwable {Object object = new Human();Human human = new Human();System.out.println(object.equals(human));}
}

如果这里的equals使用的Object类的equals方法,肯定是false

在这里插入图片描述

但是实际上的执行结果是true
在这里插入图片描述

说明已经调用了子类重写之后的方法

equals方法

本质上是为了判断两个引用类型的对象/两个基本类型的数值是否相等

equals用于基本类型

判断两个数值是否相等

        char a = 'a';byte aa = 97;System.out.println(a == aa);// 这里发生了自动类型提升,char和byte都可以无损失地转为int,a的ascii值就是97,所以是true

结果是true

equals用于引用类型

        Human h1 = new Human();h1.setAge(1);h1.setName("rainbowSea");Human h2 = new Human();h2.setAge(1);h2.setName("rainbowSea");System.out.println(h1.equals(h2));

上面讲了,这里重写了,比较的是age和name的值是否相等,所以是true

如果不重写,比较的就是内存地址,必然是false

如何重写equals方法

但是怎么重写,其实大有讲究,我们以String类重写的equals方法以及Human类重写的equals方法为例,展开说说

String类的equals方法:

/*** Compares this string to the specified object.  The result is {@code* true} if and only if the argument is not {@code null} and is a {@code* String} object that represents the same sequence of characters as this* object.** @param  anObject*         The object to compare this {@code String} against** @return  {@code true} if the given object represents a {@code String}*          equivalent to this string, {@code false} otherwise** @see  #compareTo(String)* @see  #equalsIgnoreCase(String)*/public boolean equals(Object anObject) {// 先比较两个对象是否是同一个对象(即内存地址是否相等)if (this == anObject) {// 若相等,直接返回truereturn true;}// 再比较两个对象的字符串值,这一步有很多技巧// 1. 先判断这个对象是不是String类的对象,不是直接返回falseif (anObject instanceof String) {// 2. 再判断两个字符串长度是否相等,不相等返回falseString anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {// 3. 是String对象,而且字符串长度也相等,没办法了,只能判断字符串内容了char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {// 不用比较全部的字符串,而是从头到尾逐个对比,发现不同直接返回falseif (v1[i] != v2[i])return false;i++;}// 内容相同,返回truereturn true;}}// 不是String类的对象return false;}

三部分筛选,环环相扣,逻辑严谨(写在前面的判断一定是后面判断的前提),代码简洁

强烈建议每一位读者多去读源码,不管是什么项目的,JDK,MySQL,Spring,MyBatis……都可以读,非常有帮助!

IDEA自动生成的equals方法

    @Overridepublic boolean equals(Object o) {// 1. 和String一样,先判断是不是相同引用if (this == o) return true;// 2. 再判断是不是这个类的对象if (!(o instanceof Human)) return false;Human human = (Human) o;// 3. 先比较年龄,再比较姓名,使用三元运算符简化代码if (getAge() != human.getAge()) return false;return getName() != null ? getName().equals(human.getName()) : human.getName() == null;}

hashCode方法

HashSet集合

可以用于去重,存取顺序不同

HashSet集合的add方法

HashSet可以去重,主要就是靠add方法实现

    public static void main(String[] args) throws Throwable {HashSet<Integer> hashSet = new HashSet<>();hashSet.add(1);hashSet.add(1);System.out.println(hashSet);}

在这里插入图片描述
只有一个1,说明去重了

在调用HashSet的add方法时,会先进行判断,如果add的对象之前添加过,就不会添加进去,以此达到去重的目的

那么add方法是通过何种办法判断对象相等?它和hashCode方法又有什么关系呢?

源码讲解

add方法调用了put方法
在这里插入图片描述

put方法又调用了putVal,putVal又调用了hash()
在这里插入图片描述

hash又调用了hashCode方法,null统一都是0,所以只能插入一次;不是null,则让对象的哈希值与哈希值无符号右移16位后的值进行异或运算,返回运算后的结果
在这里插入图片描述
hashCode方法不能再往下点了,native表示用C++实现
在这里插入图片描述

看到这里我们可以大胆猜测,add方法能去重,就是因为每次添加的时候都会计算添加对象的hashCode方法的哈希值,如果计算的哈希值相等,就说明这是同一个对象,就不予添加;反之,则添加

hashCode可以将任何一个对象转化为一个int类型的值,相同的对象会转换位相同的哈希值,不同的对象会被映射成不同的哈希值

为什么重写equals方法后,一定要重写hashCode方法

Java当中比较对象/数值是否相等,一般有3种:==,equals,hashCode

==

==没什么好说的,比较基本类型数值的时候还算是有用,比较引用类型是否相等,就非常的强硬,只能比较地址,意思就是“如果两个对象不是同一个对象,那么这两个对象就是不相等的

但是我们实际生活中,定义两个东西是不是相同的,概念没有这么严格,我买了两个盒装的13600kf的CPU,他们价格一样,性能参数一样,甚至体质和超频潜力都是一模一样的,尽管他们并不是同一个CPU,但是我们基本上都认为这两个CPU是“等价”的

其他的例子还有很多,在这里不再赘述

由此,我们引出了重写的equals方法

equals方法

只能用于引用类型的对象,不能用于基本类型

刚才说了,我们现实生活中,不会有这么严格的比较相等的标准,而重写Object的equals方法,就满足了我们的这个需求。当两个对象是同一个类的对象,并且所有的成员变量的属性值都是相等的,那么我们就可以认为,这两个对象是相等的

重写的equals方法,重写逻辑都是相同的:

  1. 是否是同一个对象引用
  2. 是否是同一个类型
  3. 逐个比较属性值是否相等

hashCode方法

既可以用于基本类型,也可以用于引用类型

hashCode源码简单分析

get_next_hash() 方法会根据 hashCode 的取值来决定采用哪一种哈希值的生成策略。
默认的hashCode为5
1~5分别是:随机数,根据内存指针计算,恒定2,自增,xor-shift算法

static inline intptr_t get_next_hash(Thread * Self, oop obj) {intptr_t value = 0 ;if (hashCode == 0) {// 返回随机数value = os::random() ;} elseif (hashCode == 1) {//用对象的内存地址根据某种算法进行计算intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;} elseif (hashCode == 2) {// 始终返回1,用于测试value = 1 ;            } elseif (hashCode == 3) {//从0开始计算哈希值value = ++GVars.hcSequence ;} elseif (hashCode == 4) {//输出对象的内存地址value = cast_from_oop<intptr_t>(obj) ;} else {// 默认的hashCode生成算法,利用xor-shift算法产生伪随机数unsigned t = Self->_hashStateX ;t ^= (t << 11) ;Self->_hashStateX = Self->_hashStateY ;Self->_hashStateY = Self->_hashStateZ ;Self->_hashStateZ = Self->_hashStateW ;unsigned v = Self->_hashStateW ;v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;Self->_hashStateW = v ;value = v ;}value &= markOopDesc::hash_mask;if (value == 0) value = 0xBAD ;assert (value != markOopDesc::no_hash, "invariant") ;TEVENT (hashCode: GENERATE) ;return value;
}

未被重写的hashCode方法,使用的就是5,也就是xor-shift算法,去生成伪随机数的哈希值

Marsaglia’s xor-shift 随机数生成法是一种快速并且散列性好的哈希算法

参考文章
Object中的hashCode()终于搞懂了!!!

9.1 xorshift算法

JDK核心JAVA源码解析(9) - hashcode 方法

默认hashCode全局变量的值是5,也就是说会走到这里

在这里插入图片描述

我们不需要看懂它在干什么,只需要知道一件事情:
它对相同的输入,一定是相同的输出;不同的输入,大概率是不同的输出

不知道为什么是大概率的读者可以自行搜索哈希算法,哈希碰撞,这里只做简单解释:

用有限的输出去映射无限的输入,只要输入的足够多,一定会发生哈希碰撞。

但是我们可以优化算法,尽量降低哈希碰撞在小数据量时候的发生的概率

讲了这么多,我们回归正题,hashCode也是一种判断是否相等的办法,而且非常高效,至少比equals快得多,但是hashCode方法不靠谱,有时候不相等也会认为是相等的

结论

关于重写equals方法,为什么一定要重写hashCode方法

hashCode计算虽然非常快,但是不靠谱,所以我们一般采用以下策略,去判断两个对象是否相等(这样效率很高):

  1. hashCode如果不相等,那么一定不是同一个对象,直接return false
  2. hashCode如果相等,再去调用equals方法,如果equals方法结果不相等,返回false
  3. 但是如果equals方法显示也相等,就说明出现了哈希碰撞,此时就需要在哈希表的数组对应位置生成链表;链表过长的时候还会转成红黑树,加快查询效率

所以这个方法重写的关联关系,本质上是因为人们约定俗成的一个规定:

equals方法相等,说明两个对象相等;两个对象相等,则hashCode一定相等;
hashCode不相等,则肯定不是相等的对象

equals方法是保底机制,但是比较慢

使用这种比较机制,可以大大加快哈希表数据结构key的地址的计算

如果只重写equals方法,不重写hashCode方法,就会出现“用户认为是相等的对象,hashCode值却不相等

所以我们才要一起重写hashCode方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/3227.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Promise面试题

promise与 fetch、async/await_fetch async 获取结束标志-CSDN博客 手写promise A、catch、finally、all、allsettled、any、race-CSDN博客 【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理) - 掘金 目录 每隔1秒输出1,2,3 红黄绿灯交替亮 按序执行数组 …

Flink 实时数仓(一)【实时数仓离线数仓对比】

前言 昨天技术面的时候&#xff0c;面试官说人家公司现在用的都是最新的技术&#xff0c;比如 Doris 等一些最新的工具&#xff0c;确实这些课是学校永远不会开设的&#xff0c;好在他说去了会带着我做一做。可是 ...... 学院舍不得让走啊 ...... 没办法&#xff0c;情况就是这…

img 图片加载失败后展示本地展示

Vue3 图片预览实现网络图片请求失败后加载本地图片 html 代码&#xff1a; <img v-if"logo" :src"imgSrc" error"handleError" />script 代码&#xff1a; import localImg from /assets/logo.pngconst networkImg https://img1.baidu.…

python代码报错requests.exceptions.SSLError

直接浏览器访问报ssl错误的网站&#xff0c;然后找到证书&#xff0c;选择导出 然后在cmd里面执行&#xff0c;去查看certifi的位置 python -c "import certifi;print(certifi.where())"找到之后用文本编辑器打开&#xff0c;我用的vscode 滚动到最后面&#xff0c…

长期供应KLM8G1GETF-B041 存储芯片

长期供应各品牌芯片&#xff1a; H58GG6MK6GX037 BK1005HS601-T BKP1005HS221-T MMUN2233LT1G GJM0335C1ER70BB01D NCP115ASN180T2G CC0402KRX5R6BB224 GRM033R71A472KA01D MMSZ4678T1G MMSZ4686T1G MMSZ4691T1G MMSZ4704T1G MMSZ5243BT1G MMSZ4709T1G GRM1555C…

DPDK UDP通信

1. 编译运行程序 环境配置&#xff1a; win10 运行 socket 客户端工具 Linux DPDK 运行 UDP 程序 注意事项&#xff1a; DPDK 跳过内核协议栈&#xff0c;所以 ARP 协议也不支持&#xff0c;需要手动在 win10 上配置静态 arp 地址&#xff0c;保证数据包发到网卡。 netsh i i …

什么是重放攻击(Reply attack)?

什么是重放攻击(Reply attack)? 重放攻击&#xff0c;也称为回放攻击&#xff0c;是一种网络攻击方式。重放攻击是一种中间人攻击&#xff0c;攻击者通过截获合法的数据传输并重新发送它们来欺骗接收方&#xff0c;让接收方误以为是合法的消息。重放攻击是非常常见的&#xf…

在IDEA中使用.env文件导入系统配置的图文教程

JetBrains的IDEA是一款功能强大的集成开发环境&#xff0c;为开发人员提供了丰富的功能和工具。使用.env文件来管理配置信息在IDEA中非常简单。 旧版本默认支持&#xff0c;新版本idea需要安装插件才可以。 这里我们可以安装EnvFile插件&#xff0c;步骤如下&#xff1a; 在弹…

BarTransitions

NavigationBarTransitions的作用 BarTransitions 有以下7中模式&#xff1a; public static final int MODE_TRANSPARENT 0; 全透明public static final int MODE_SEMI_TRANSPARENT 1; 半透明public static final int MODE_TRANSLUCENT 2;public static final int MODE_LIG…

详解数仓的向量化执行引擎

前言 适用版本&#xff1a;【基线功能】 传统的行执行引擎大多采用一次一元组的执行模式&#xff0c;这样在执行过程中CPU大部分时间并没有用来处理数据&#xff0c;更多的是在遍历执行树&#xff0c;就会导致CPU的有效利用率较低。而在面对OLAP场景巨量的函数调用次数&#x…

ctfshow web29-web40

命令执行 看清都过滤了些什么&#xff01;&#xff01; 知识点&#xff1a; web34&#xff1a;当;和()被过滤了就用语言结构&#xff0c;一般有echo print isset unset include require web37&#xff1a;data协议是将后面的字符串当成php代码执行&#xff0c;例如 /?cdat…

JVM学习笔记(四)类加载与字节码技术

目录 一、类文件结构 二、字节码指令 2.3 图解方法执行流程 1&#xff09;原始 java 代码 2&#xff09;编译后的字节码文件 3&#xff09;常量池载入运行时常量池 4&#xff09;方法字节码载入方法区 5&#xff09;main 线程开始运行&#xff0c;分配栈帧内存 6&…

python语音版东北方言小词典

目录 一.前言 二.代码 三.分析 一.前言 东北方言是中国境内最为特色和独特的方言之一,它主要分布在中国东北地区的吉林、辽宁和黑龙江省。这个地区被称为“东北三省”,所以也被称为“东北话”或“东北官话”。 东北方言与普通话(官话)有着明显的差异,包括发音、词汇…

甘特图是什么?如何利用其优化项目管理流程?

甘特图是项目管理软件中十分常见的功能&#xff0c;可以说每一个项目经理都要学会使用甘特图才能更好的交付项目。什么是甘特图&#xff1f;甘特图用来做什么&#xff1f;简单来说一种将项目任务与时间关系直观表示的图表&#xff0c;直观地展示了任务进度和持续时间。 一、甘特…

【C++】string类的增删改查模拟实现(图例超详细解析!!!)

目录 一、前言 二、string类的模拟实现 ✨前情提要 ✨Member functions —— 成员函数 ⚡构造函数 ⚡拷贝构造函数 ⚡赋值运算符重载 ⚡析构函数 ✨Element access —— 元素访问 ⚡operator[ ] ⚡Iterator —— 迭代器 ✨Capacity —— 容量 ⚡size ⚡capacity ⚡clea…

井字棋源码(网络线程池版)

源码链接&#xff1a;game 效果可能没有那么好&#xff0c;大家可以给点建议。 效果展示 game.h #include <stdio.h> #include <stdlib.h> #include <time.h>#define ROW 3 #define COL 3void InitBoard(char board[ROW][COL], int row, int col) {int i…

企业数字化转型,“业务”先行

在当今时代&#xff0c;数字化转型已经成为企业发展的必经之路。数字化转型&#xff0c;简而言之&#xff0c;就是运用数字技术&#xff0c;对企业运营管理的各个环节进行深度改造&#xff0c;以提升企业的运营效率和市场竞争力。据有关机构研究测算&#xff0c;数字化转型可使…

丈母娘眼中“靠谱女婿”职业榜曝光,公务员跌落榜首,新兴职业成宠儿!

正如婆婆和媳妇相处很复杂&#xff0c;丈母娘和女婿亦有着微妙关系&#xff0c;看对眼是“半个儿”&#xff0c;不对付则会成为小两口婚姻的“地雷”&#xff0c;甚至是恋爱路上的“拦路虎”。 近来&#xff0c;最新丈母娘认可的“靠谱女婿”职业榜排行新鲜出炉&#xff0c;备受…

R语言 数据的整理与清洗(Data Frame 篇下)

《Cookbook for R》 Manipulating Data ~ Dataframe Comparing data frames 比较数据框 当你想比较两个或多个数据框&#xff0c;并从中找到&#xff1a; 1、重复出现在多个数据框中的行 2、或仅出现在一个数据框中的行 先构建三个示例数据框 dfA <- data.frame(Subjectc…

5g工业数采网关是什么?-天拓四方

随着工业4.0时代的到来&#xff0c;数字化、网络化、智能化成为工业发展的新趋势。在这个过程中&#xff0c;5G工业数采网关作为一种关键设备&#xff0c;发挥着越来越重要的作用。本文将详细解析5G工业数采网关是什么&#xff0c;以及它在工业领域中的应用和重要性。 一、5G工…