当前位置: 首页 > news >正文

通用方法——为什么重写equals还要重写hashcode

本文介绍java.lang.Object类中的两个方法:equals和hashCode。这两个方法大家应该都知道,但是这两个方法的作用是什么、为什么重写equals还要重写hashCode、它们之间有什么关系和约定等,今天就来带大家了解一下。

1、hashCode

hashCode即散列码。散列码是用一个int值来代表对象,它是通过将该对象的某些信息进行转换而生成的。Object类中默认的hashCode方法如下

public native int hashCode();

这是一个本地方法,不同的虚拟机有不同的实现(具体实现自己看虚拟机源码哈)。Object默认的hashCode是根据对象的内存地址转化而来的,它是唯一的

我们可以在自己的类中覆盖hashCode方法,但我们可以使用System.identityHashCode(Object x)方法返回默认的hashcode,无论对象是否覆盖默认的hashcode

hashCode方法主要是为了给诸如HashMap这样的哈希表使用。

设计hashcode最重要的因素是:对同一个对象调用hachCode()应该产生同样的值(前提是对象的信息没有被改变)设计一个hashCode,它必须快,而且具有意义(使用有意义的字段来生成hashcode)。hashCode不需要唯一(默认的hashCode唯一),因此更应该关注它的速度,而不是唯一性。

由于在生成桶(桶指哈希桶,或哈希表的槽位)的下标前,hashcode还要做进一步处理,所以生成的hashCode范围不是很重要,是int就行。好的hashCode()应该产生分布均匀的散列码。

哈希桶的大小最好是2的n次方。

对现代处理器来说,除数和求余是最慢的操作,而使用2的n次方,可以用位运算代替求余(%开销较大)。

举个例子,假设哈希桶大小为16(HashMap初始大小),假设hashCode为20,那么使用%求余会得到下标4;但这可以用 hashCode&(length-1) 代替,即20&(16-1),结果也是4


2、equals

hashCode并不需要唯一性,但equals必须严格地判断两个对象是否相同。

正确的equals方法有如下特性:

自反性:x.equals(x)一定返回true

对称性:如果x.equals(y)为true,那么y.equals(x)也为true

传递性:如果x.equals(y)为true、y.equals(z)为true,那么x.equals(z)也为true

一致性:如果x和y中用于等价比较的信息没有改变,那么x.equals(y)无论调用多少次,结果都一致

任何不是null的x,x.equals(null)一定返回false


3、根据Object规范,规范约定:

  1. 如果两个对象通过equals方法比较是相等的,那么它们的hashCode方法结果值也是相等的。
  2. 如果两个对象通过equals方法比较是不相等的,那么不要求它们的hashCode方法结果值是相等的。
  3. 当在一个应用程序执行过程中, 如果equals方法比较中没有修改任何信息,那么在同一个对象上重复调用hashCode方法时,它必须始终返回相同的值。但如果从一个应用程序到了另一个应用程序,两个应用程序汇中调用hashCode方法的返回值可以是不一致的。

Object类中的默认的equals和hashCode方法:

  1. equals:比较的是对象的内存地址是否相同(相当于==操作符)
  2. hashCode:hashCode方法的返回值符合上述规范

因此,当只重写equals方法,不重写hashCode时,违反规定:equals相等的对象必须具有相等的哈希码。

如果不这样做,你的类违反了hashCode的通用约定,对于HashSet, HashMap, HashTable等基于hash值的类就会出现问题。

以HashMap为例,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
这样解决了向含有大量数据的集合中添加元素时,大量频繁的操作equals方法的问题。

下面举个例子说明:
创建一个Point类,有两个成员变量x和y,并重写了equals方法。

public class Point {private final int x, y;public Point(int x, int y) {this.x = x;this.y = y;}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (!(obj instanceof Point)) return false;Point point = (Point) obj;return x == point.x && y == point.y;}public static void main(String[] args) {Point p1 = new Point(1, 2);Point p2 = new Point(1, 2);System.out.println(p1.equals(p2));// trueMap<Point, String> map = new HashMap<>();map.put(p1, "p1");System.out.println(map.get(p2)); // null}
}

你可能觉得 map.get(p2) 应该返回字符串 p1, 但是却返回null, 这是因为Point类并没有重写hashCode方法,导致两个相等的实例p1和p2返回了不同的哈希码,违反了hashCode的约定,put方法把实例p1放到了一个哈希桶(hash bucket)中,但因为p2的哈希码不等于p1的哈希码,所以get方法会从其它哈希桶中去查找。

解决这个方法很简单,只需要重写Point类的hashCode方法。

	@Overridepublic int hashCode() {int result = Integer.hashCode(x);result = 31 * result + Integer.hashCode(y);return result;}public static void main(String[] args) {Point p1 = new Point(1, 2);Point p2 = new Point(1, 2);System.out.println(p1.equals(p2));// trueMap<Point, String> map = new HashMap<>();map.put(p1, "p1");System.out.println(map.get(p2)); // p1}

这次你会发现map.get(p2) 返回的就是字符串p1了, 因为hashCode这个方法会返回一个简单的确定性计算的结果,它的唯一的输入是 Point实例中的两个重要的属性x和y,所以显然相等的 Point实例具有相同的哈希码。

此外Objects 类有一个静态方法,它接受任意数量的对象并为它们返回一个哈希码。这个名为 hash 的方法可以 让你编写一行 hashCode 方法,其质量与根据这个项目中的上面编写的方法相当。

	@Overridepublic int hashCode() {return Objects.hash(x, y);}

注意事项

  1. 当你写完 hashCode 方法后,请一定问一下自己是否满足相等的实例有相同的哈希码这一条件。
  2. hashCode中涉及到的属性应与equals中保持一致,不要试图从哈希码计算中排除重要的属性来提高性能。

总之,每次重写 equals 方法时都必须重写 hashCode 方法,否则程序将无法正常运行。你的 hashCode 方 法必须遵从 Object 类指定的常规约定,并且必须执行合理的工作,将不相等的哈希码分配给不相等的实例

相关文章:

通用方法——为什么重写equals还要重写hashcode

本文介绍java.lang.Object类中的两个方法&#xff1a;equals和hashCode。这两个方法大家应该都知道&#xff0c;但是这两个方法的作用是什么、为什么重写equals还要重写hashCode、它们之间有什么关系和约定等&#xff0c;今天就来带大家了解一下。 1、hashCode hashCode即散列…...

JavaSE学习进阶day2_01 包和权限修饰符

第一章 包 1.1 包 包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术&#xff0c;不同的技术类放在不同的包下&#xff0c;方便管理和维护。 在IDEA项目中&#xff0c;建包的操作如下&#xff1a; 这个咱们在基础班就谈到过。 包名的命名规范&#xff1a; 路径…...

Android性能调优 - 省电优化

省电&#xff1a;通过工具Battery Historian查看到:耗电大头: 屏幕、网络、cpuled/oled屏幕显示:降低亮度&#xff0c;开深色模式&#xff1b;锁屏间隔缩短到 &#xff1b;亮屏需要一直持有唤醒锁&#xff0c;还有gps定位也需要用到唤醒锁;网络&#xff1a; 常用的网络优化措施…...

ElasticSearch - SpringBoot整合ES之全文搜索匹配查询 match

文章目录1. 数据准备2. match 匹配查询1. 全文检索2. 简化查询DSL语句3. match 匹配查询原理官方文档地址&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/index.html权威指南&#xff1a;https://www.elastic.co/guide/cn/elasticsearch/guide/current/…...

句子的改写和扩写

目录 1.句子改写 2.句子扩写 &#xff08;不低于15个句子算是长句子&#xff0c;不能太多长句子&#xff09; 1.句子改写 我绝不会嫁给你的。 如果你是世界上最后一个男人&#xff0c;我就去寺庙。 If you married me,I would jump into the well. 如果你嫁给我&#xff0c;我…...

DockerFile创建及案例

DockerFile dockerfile是用来构建docker镜像的文件&#xff0c;命令脚本参数脚本&#xff01; 构建步骤 编写一个dockerfile文件docker build 构建成为一个对象docker run 运行镜像docker push 发布镜像&#xff08;DockerHub、阿里云镜像仓库&#xff09; 去官网Docker-Hub…...

第十四届蓝桥杯三月真题刷题训练——第 1 天

目录 题目1&#xff1a;数列求值 代码&#xff1a; 题目2&#xff1a;质数 代码&#xff1a; 题目3&#xff1a;饮料换购 代码&#xff1a; 题目1&#xff1a;数列求值 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出…...

基于容器云提交spark job任务

容器云提交spark job任务 容器云提交KindJob类型的spark任务&#xff0c;首先需要申请具有Job任务提交权限的rbac&#xff0c;然后编写对应的yaml文件&#xff0c;通过spark内置的spark-submit命令&#xff0c;提交用户程序(jar包)到集群执行。 1、创建任务job提交权限rbac …...

Linux系统调用之目录操作函数

前言 如果&#xff0c;想要深入的学习Linux系统调用中mkdir&#xff0c;rmdir&#xff0c;rename&#xff0c;chdir&#xff0c;getcwd等这些有关于目录操作函数&#xff0c;还是需要去自己阅读Linux系统中的帮助文档。 具体输入命令&#xff1a; man 2 mkdir/rmdir/rename/ch…...

设计模式-策略模式

前言 作为一名合格的前端开发工程师&#xff0c;全面的掌握面向对象的设计思想非常重要&#xff0c;而“设计模式”是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的&#xff0c;代表了面向对象设计思想的最佳实践。正如《HeadFirst设计模式》中说的一句话&…...

面试+算法:罗马数字及Excel列名与数字互相转换

概述 算法是一个程序员的核心竞争力&#xff0c;也是面试最重要的考查环节。 试题 判断一个罗马数字是否有效 罗马数字包含七种字符&#xff1a;I&#xff0c;V&#xff0c;X&#xff0c;L&#xff0c;C&#xff0c;D和M&#xff0c;如下 字符数值I1V5X10L50C100D500M1000…...

Connext DDS路由服务Routing Service(1)

1 简介 RTI路由服务是一种开箱即用的解决方案,允许开发人员快速扩展和集成不同或地理位置分散的实时系统。它跨域、LAN和WAN扩展RTI ConnextDDS应用程序,包括防火墙和NAT穿越。 它还支持DDS到DDS的桥接,允许您对数据进行转换。这允许未修改的DDS应用程序进行通信,即使它们是…...

如何使用SaleSmartly进行Facebook Messenger 营销、销售和支持

如何使用SaleSmartly&#xff08;ss客服&#xff09;进行Facebook Messenger 营销、销售和支持上篇文章我们讲了什么是Facebook Messenger CRM以及获得Facebook Messenger CRM的注意事项&#xff0c;现在你有更多时间与客户聊天&#xff0c;让我们看看你如何使用SaleSmartly&am…...

教资教育知识与能力中学教学

目录 3.1 教学概述 3.2 教学过程 3.3 教学原则*【简答/辨析重点】 3.4 教学方法 3.5 教学组织形式 3.6 教学工作基本环节 3.7 教学评价 3.1 教学概述 1、教学的意义【14/18辨析】 教学是传授系统知识、促进学生发展的最有效形式&#xff1b; 教学是学校进行全面发展教…...

IDEA中使用Tomcat的两种方式:集成本地Tomcat使用Tomcat Maven插件

一、前言 在IDEA中创建完一个Maven Web项目&#xff0c;并补齐了目录以后&#xff0c;准备使用Tomcat时&#xff0c;就需要在自己创建的项目中去部署Tomcat&#xff0c;前文已经介绍了如何创建Maven Web&#xff0c;所以这里就不多加赘述&#xff0c;直接讲述部署Tomcat的方法…...

IP 地址的简介

IP 地址 Internet 依靠 TCP/IP 协议&#xff0c;在全球范围内实现不同硬件结构、不同操作系统、不同网络系统的主机之间的互联。在 Internet 上&#xff0c;每一个节点都依靠唯一的 IP 地址相互区分和相互联系&#xff0c;IP 地址用于标识互联网中的每台主机的身份&#xff0c…...

3D动作/动画特效

硕士/博士符合一本高校人才引进条件的硕士、博士&#xff0c;教研能力突出者可签合作高校正式编制本科/硕士成绩优异专业扎实、有创新思维者可在签约工作后在校继续读研读博【产业模式】数字经济→数字孪生→升级转型【细份领域】数字产业、数字工程、数字教研、数字政企【合作…...

python 多线程编程之_thread模块

_thread模块除了可以派生线程外&#xff0c;还提供了基本的同步数据结构&#xff0c;又称为锁对象&#xff08;lock object&#xff0c;也叫原语锁、简单锁、互斥锁、互斥和二进制信号量&#xff09;。 下面是常用的线程函数&#xff1a; 函数描述start_new_thread(function,…...

vue:vue2与vue3的区别

一、背景 vue2是指的2.X vue3是指的3.0以及更新的版本&#xff08;3.2版本在script标签里可以写setup&#xff0c;极大的简化了开发&#xff09; 本文对比两者区别。 二、官网 生命周期选项 | Vue.js API 参考 | Vue.js Vue.js - 渐进式 JavaScript 框架 | Vue.js Vue.…...

SQL数据库语法

目录 1. 常用数据类型 2. 约束 4. 数据库操作 5. 数据表操作 1. 常用数据类型 int 整型double 浮点数varchar 字符型data 年月日datetime 年月日 时分秒2. 约束 主键 primary key : 物理上存储的顺序(存在真实排序), 主键…...

人机界面艺术设计

人机界面艺术设计 2.1人机界面艺术设计思路 人们经常有意通过某种工具或创造来解决难题&#xff0c;然而这并不意味着人们乐于接受别人或其他事情&#xff0c;他们很难提出问题。在用户使用网页或软件的时候&#xff0c;他们有明确的目标&#xff0c;他们利用电脑来帮助自己达…...

【办公类-19-01-02】办公中的思考——Python,统计教职工的姓名中那些字最多?

背景需求&#xff1a;上一篇计算了教职工的姓氏谁最多&#xff0c;col[0]]这一篇统计教职工的&#xff08;姓氏名字&#xff09;里面哪些字出现最多。材料准备&#xff1a;1、下载所有员工名单写代码。py 包含”姓氏名字“的重字率统计from pandas import DataFrame, Series im…...

HCIP实验1

实验要求 1 R6为isp, 接口IP地址均为公有地址;该设备只能配置IP地址&#xff0c;之后不能冉对其进行其他任何配置; 2 R1-R5为局域网&#xff0c;私有IP地址192.168.1.0/24&#xff0c; 请合理分配; 3 R1, R2, R4,各有两个环回地址; R5; R6各有一个环回地址;所有路由器上环回均…...

一个Bug让人类科技倒退几十年?

大家好&#xff0c;我是良许。 前几天在直播的时候&#xff0c;问了直播间的小伙伴有没人知道「千年虫」这种神奇的「生物」的&#xff0c;居然没有一人能够答得上来的。 所以&#xff0c;今天就跟大家科普一下这个人类历史上最大的 Bug 。 1. 全世界的恐慌 一个Bug会让人类…...

2023王道考研数据结构笔记第四章串

第四章 串 4.1 串的定义 4.1.1 串的相关概念 串&#xff1a;即字符串&#xff08;String&#xff09;是由零个或多个字符组成的有限序列。一般记为S‘a1a2…an’ (n>0) 其中S是串名&#xff0c;单引号&#xff08;注&#xff1a;有的地方用双引号&#xff0c;如Java、C&am…...

【AI绘图学习笔记】深度学习相关数学原理总结(持续更新)

如题&#xff0c;这是一篇深度学习相关数学原理总结文&#xff0c;由于深度学习中涉及到较多的概率论知识&#xff08;包括随机过程&#xff0c;信息论&#xff0c;概率与统计啥啥啥的)&#xff0c;而笔者概率知识储备属实不行&#xff0c;因此特意开一章来总结&#xff08;大部…...

CSGO服务器配置全贴纸插件方法教程

CSGO服务器配置全贴纸插件方法教程 关于插件的警告 一定要了解V社对于CSGO社区服务器的规定&#xff0c;全皮肤插件/全手套插件等&#xff0c;在设置了GSLT的情况下&#xff0c;是有可能被封禁GSLT账号的&#xff08;所以慎用&#xff09; 配置好服务器之后呢&#xff0c;想安…...

Python爬虫——使用socket模块进行图片下载

Python爬虫——使用socket模块进行图片下载什么是socket爬虫的工作流程socket爬取图片为什么能用socket能下载图片socket下载图片和request下载图片的区别使用socket下载一张图片使用socket下载多张图片方法1方法2什么是socket Socket 是一种通信机制&#xff0c;用于实现网络…...

通用游戏地图解决方案设计解析

前言&#xff1a; 在软件开发过程中&#xff0c;我们都希望能设计出一个稳健的&#xff0c;可维护的系统&#xff0c;为了实现这个目的&#xff0c;人们总结出了很多相关的设计原则&#xff0c;比如SOLID原则&#xff0c; KISS原则等等。SOLID每个字母代表了一种设计原则&…...

java @Autowired @Resource @Inject 三个注解的区别

javax.annotation.Resourcejdk 内置的&#xff0c;JSR-250 中的注解。依赖注入通过 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor 来处理。org.springframework.beans.factory.annotation.Autowired org.springframework.beans.factory.annotati…...