说说hashCode() 和 equals() 之间的关系?
每天一道面试题,陪你突击金九银十!
上一篇关于介绍Object类下的几种方法时面试题时,提到equals()和hashCode()方法可能引出关于“hashCode() 和 equals() 之间的关系?”的面试题,本篇来解析一下这道基础面试题。
先祭一张图,可以思考一下为什么?
介绍
equals()
的作用是用来判断两个对象是否相等。
hashCode()
的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
关系
我们以“类的用途”来将“hashCode() 和 equals()的关系”分2种情况来说明。
1、不会创建“类对应的散列表”
这里所说的“不会创建类对应的散列表”是说:我们不会在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结构中,用到该类。例如,不会创建该类的HashSet集合。
在这种情况下,该类的“hashCode() 和 equals() ”没有半毛钱关系的!equals() 用来比较该类的两个对象是否相等。而hashCode() 则根本没有任何作用。
下面,我们通过示例查看类的两个对象相等 以及 不等时hashCode()的取值。
import java.util.*;
import java.lang.Comparable;
/**
* @desc 比较equals() 返回true 以及 返回false时, hashCode()的值。
*
*/
public class NormalHashCodeTest{
public static void main(String[] args) {
// 新建2个相同内容的Person对象,
// 再用equals比较它们是否相等
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
Person p3 = new Person("aaa", 200);
System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
System.out.printf("p1.equals(p3) : %s; p1(%d) p3(%d)\n", p1.equals(p3), p1.hashCode(), p3.hashCode());
}
/**
* @desc Person类。
*/
private static class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " - " +age;
}
/**
* @desc 覆盖equals方法
*/
public boolean equals(Object obj){
if(obj == null){
return false;
}
//如果是同一个对象返回true,反之返回false
if(this == obj){
return true;
}
//判断是否类型相同
if(this.getClass() != obj.getClass()){
return false;
}
Person person = (Person)obj;
return name.equals(person.name) && age==person.age;
}
}
}
运行结果:
p1.equals(p2) : true; p1(1169863946) p2(1901116749)
p1.equals(p3) : false; p1(1169863946) p3(2131949076)
从结果也可以看出:p1和p2相等的情况下,hashCode()也不一定相等。
2、会创建“类对应的散列表”
这里所说的“会创建类对应的散列表”是说:我们会在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结构中,用到该类。例如,会创建该类的HashSet集合。
在这种情况下,该类的“hashCode() 和 equals() ”是有关系的:
-
如果两个对象相等,那么它们的hashCode()值一定相同。这里的相等是指,通过equals()比较两个对象时返回true。
-
如果两个对象hashCode()相等,它们并不一定相等。因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等。补充说一句:“两个不同的键值对,哈希值相等”,这就是哈希冲突。
此外,在这种情况下。若要判断两个对象是否相等,除了要覆盖equals()之外,也要覆盖hashCode()函数。否则,equals()无效。
举例,创建Person类的HashSet集合,必须同时覆盖Person类的equals() 和 hashCode()方法。
如果单单只是覆盖equals()方法。我们会发现,equals()方法没有达到我们想要的效果。
import java.util.*;
import java.lang.Comparable;
/**
* @desc 比较equals() 返回true 以及 返回false时, hashCode()的值。
*
*/
public class ConflictHashCodeTest1{
public static void main(String[] args) {
// 新建Person对象,
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
Person p3 = new Person("aaa", 200);
// 新建HashSet对象
HashSet set = new HashSet();
set.add(p1);
set.add(p2);
set.add(p3);
// 比较p1 和 p2, 并打印它们的hashCode()
System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
// 打印set
System.out.printf("set:%s\n", set);
}
/**
* @desc Person类。
*/
private static class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "("+name + ", " +age+")";
}
/**
* @desc 覆盖equals方法
*/
@Override
public boolean equals(Object obj){
if(obj == null){
return false;
}
//如果是同一个对象返回true,反之返回false
if(this == obj){
return true;
}
//判断是否类型相同
if(this.getClass() != obj.getClass()){
return false;
}
Person person = (Person)obj;
return name.equals(person.name) && age==person.age;
}
}
}
运行结果:
p1.equals(p2) : true; p1(1169863946) p2(1690552137)
set:[(eee, 100), (eee, 100), (aaa, 200)]
结果分析:
我们重写了Person的equals()。但是,很奇怪的发现:HashSet中仍然有重复元素:p1 和 p2。为什么会出现这种情况呢?
这是因为虽然p1 和 p2的内容相等,但是它们的hashCode()不等;所以,HashSet在添加p1和p2的时候,认为它们不相等。
那同时覆盖equals() 和 hashCode()方法呢?
import java.util.*;
import java.lang.Comparable;
/**
* @desc 比较equals() 返回true 以及 返回false时, hashCode()的值。
*
*/
public class ConflictHashCodeTest2{
public static void main(String[] args) {
// 新建Person对象,
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
Person p3 = new Person("aaa", 200);
Person p4 = new Person("EEE", 100);
// 新建HashSet对象
HashSet set = new HashSet();
set.add(p1);
set.add(p2);
set.add(p3);
// 比较p1 和 p2, 并打印它们的hashCode()
System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
// 比较p1 和 p4, 并打印它们的hashCode()
System.out.printf("p1.equals(p4) : %s; p1(%d) p4(%d)\n", p1.equals(p4), p1.hashCode(), p4.hashCode());
// 打印set
System.out.printf("set:%s\n", set);
}
/**
* @desc Person类。
*/
private static class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " - " +age;
}
/**
* @desc重写hashCode
*/
@Override
public int hashCode(){
int nameHash = name.toUpperCase().hashCode();
return nameHash ^ age;
}
/**
* @desc 覆盖equals方法
*/
@Override
public boolean equals(Object obj){
if(obj == null){
return false;
}
//如果是同一个对象返回true,反之返回false
if(this == obj){
return true;
}
//判断是否类型相同
if(this.getClass() != obj.getClass()){
return false;
}
Person person = (Person)obj;
return name.equals(person.name) && age==person.age;
}
}
}
运行结果:
p1.equals(p2) : true; p1(68545) p2(68545)
p1.equals(p4) : false; p1(68545) p4(68545)
set:[aaa - 200, eee - 100]
结果分析:
这下,equals()生效了,HashSet中没有重复元素。
比较p1和p2,我们发现:它们的hashCode()相等,通过equals()比较它们也返回true。所以,p1和p2被视为相等。
比较p1和p4,我们发现:虽然它们的hashCode()相等;但是,通过equals()比较它们返回false。所以,p1和p4被视为不相等。
原则
1.同一个对象(没有发生过修改)无论何时调用hashCode()得到的返回值必须一样。如果一个key对象在put的时候调用hashCode()决定了存放的位置,而在get的时候调用hashCode()得到了不一样的返回值,这个值映射到了一个和原来不一样的地方,那么肯定就找不到原来那个键值对了。
2.hashCode()的返回值相等的对象不一定相等,通过hashCode()和equals()必须能唯一确定一个对象。不相等的对象的hashCode()的结果可以相等。hashCode()在注意关注碰撞问题的时候,也要关注生成速度问题,完美hash不现实。
3.一旦重写了equals()函数(重写equals的时候还要注意要满足自反性、对称性、传递性、一致性),就必须重写hashCode()函数。而且hashCode()的生成哈希值的依据应该是equals()中用来比较是否相等的字段。
如果两个由equals()规定相等的对象生成的hashCode不等,对于hashMap来说,他们很可能分别映射到不同位置,没有调用equals()比较是否相等的机会,两个实际上相等的对象可能被插入不同位置,出现错误。其他一些基于哈希方法的集合类可能也会有这个问题
顶尖架构师栈
关注回复关键字
【C01】超10G后端学习面试资源
【IDEA】最新IDEA激活工具和码及教程
【JetBrains软件名】 最新软件激活工具和码及教程
工具&码&教程
转载于:https://mp.weixin.qq.com/s/vdCW5ZwZUJ3oe9-qLVGnZw
本文由 mdnice 多平台发布
相关文章:
说说hashCode() 和 equals() 之间的关系?
每天一道面试题,陪你突击金九银十! 上一篇关于介绍Object类下的几种方法时面试题时,提到equals()和hashCode()方法可能引出关于“hashCode() 和 equals() 之间的关系?”的面试题,本篇来解析一下这道基础面试题。 先祭一…...
算法通关村-----图的基本算法
图的实现方式 邻接矩阵 定义 邻接矩阵是一个二维数组,其中的元素表示图中节点之间的关系。通常,如果节点 i 与节点 j 之间有边(无向图)或者从节点 i 到节点 j 有边(有向图),则矩阵中的元素值…...
基于随机森林+小型智能健康推荐助手(心脏病+慢性肾病健康预测+药物推荐)——机器学习算法应用(含Python工程源码)+数据集(二)
目录 前言总体设计运行环境Python环境依赖库 模块实现1. 疾病预测2. 药物推荐1)数据预处理2)模型训练及应用3)模型应用 其它相关博客工程源代码下载其它资料下载 前言 本项目基于Kaggle上公开的数据集,旨在对心脏病和慢性肾病进行…...
stm32学习-芯片系列/选型
【03】STM32HAL库开发-初识STM32 | STM概念、芯片分类、命名规则、选型 | STM32原理图设计、看数据手册、最小系统的组成 、STM32IO分配_小浪宝宝的博客-CSDN博客 STM32:ST是意法半导体,M是MCU/MPU,32是32位。 ST累计推出了:…...
LeetCode //C - 200. Number of Islands
200. Number of Islands Given an m x n 2D binary grid grid which represents a map of *‘1’*s (land) and *‘0’*s (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically…...
使用Python构建强大的网络爬虫
介绍 网络爬虫是从网站收集数据的强大技术,而Python是这项任务中最流行的语言之一。然而,构建一个强大的网络爬虫不仅仅涉及到获取网页并解析其HTML。在本文中,我们将为您介绍创建一个网络爬虫的过程,这个爬虫不仅可以获取和保存网…...
图像处理之《基于语义对象轮廓自动生成的生成隐写术》论文精读
一、相关知识 首先我们需要了解传统隐写和生成式隐写的基本过程和区别。传统隐写需要选定一幅封面图像,然后使用某种隐写算法比如LSB、PVD、DCT等对像素进行修改将秘密嵌入到封面图像中得到含密图像,通过信道传输后再利用算法的逆过程提出秘密信息。而生…...
Java 字节流
一、输入输出流 输入输出 ------- 读写文件 输入 ------- 从文件中获取数据到自己的程序中,接收处理【读】 输出 ------- 将自己程序中处理好的数据保存到文件中【写】 流 ------- 数据移动的轨迹 二、流的分类 按照数据的移动轨迹分为:输入流 输出流…...
华硕电脑怎么录屏?分享实用录制经验!
“华硕电脑怎么录屏呀,刚买的笔记本电脑,是华硕的,自我感觉挺好用的,但是不知道怎么录屏,最近刚好要录一个教程,怎么都找不到在哪里录制,有人能教教我吗?” 随着电脑技术的不断发展…...
python学习--python的异常处理机制
try…except try:n1int(input(请输入一个整数))n2int(input(请输入另一个整数))resultn1/n2print(结果为,result) except ZeroDivisionError: print(除数不能为0)try…except…else 如果try块中没有抛出异常,则执行else块,如果try中抛出异常࿰…...
nacos+Dubbo整合快速入门
官网:Nacos Spring Boot 快速开始 下载下载链接启动:进入bin目录,startup.cmd -m standalone引入依赖 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.9…...
QT实现钟表
1、 头文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QPaintEvent> //绘制事件类 #include <QDebug> //信息调试类 #include <QPainter> //画家类 #include <QTimerEve…...
准备我们心爱的IDEA写Jsp
JSP学习 一、准备我们心爱的IDEA new一个项目:New Project --> Next -->Next -->Finsh 二、配置好服务器Tomcat-9.0.30 1.> 在WEB-INF下创建一个Lib包 将jsp-api.jar复制进去,并使其生效 未生效前: 生效过程: 2.>…...
将近 5 万字讲解 Python Django 框架详细知识点(更新中)
Django 框架基本概述 Django 是一个开源的 Web 应用后端框架,由 Python 编写。它采用了 MVC 的软件设计模式,即模型(Model)、视图(View)和控制器(Controller)。在 Django 框架中&am…...
Arcgis提取每个像元的多波段反射率值
Arcgis提取每个像元的多波段反射率值 数据预处理 数据预处理阶段需要对遥感图像进行编辑传感器参数、辐射定标、大气校正、正射校正,具体流程见该文章 裁剪研究区 对于ENVI处理得到的tiff影像,虽然是经过裁剪了,但是还存在黑色的背景值&a…...
JavaScript面试题整理(一)
数据类型篇 1、JavaScript有哪些数据类型,它们的区别是什么? 基本数据类型:number、string、boolean、undefined、NaN、BigInt、Symbol 引入数据类型:Object NaN是JS中的特殊值,表示非数字,NaN不是数字…...
数据结构:树和二叉树之-堆排列 (万字详解)
目录 树概念及结构 1.1树的概念 1.2树的表示 编辑2.二叉树概念及结构 2.1概念 2.2数据结构中的二叉树:编辑 2.3特殊的二叉树: 编辑 2.4 二叉树的存储结构 2.4.1 顺序存储: 2.4.2 链式存储: 二叉树的实现及大小堆…...
爬虫入门基础:深入解析HTTP协议的工作过程
目录 一、HTTP协议简介 二、HTTP协议的工作过程 三、请求方法与常见用途 四、请求头与常见字段 五、状态码与常见含义 六、进阶话题和注意事项 总结 在如今这个数字化时代,互联网已经成为我们获取信息、交流和娱乐的主要渠道。而在互联网中,HTTP协…...
k8备份与恢复-Velero
简介 Velero 是一款可以安全的备份、恢复和迁移 Kubernetes 集群资源和持久卷等资源的备份恢复软件。 Velero 实现的 kubernetes 资源备份能力,可以轻松实现 Kubernetes 集群的数据备份和恢复、复制 kubernetes 集群资源到其他kubernetes 集群或者快速复制生产环境…...
基于Python开发的火车票分析助手(源码+可执行程序+程序配置说明书+程序使用说明书)
一、项目简介 本项目是一套基于Python开发的火车票分析助手,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含:项目源码、项目文档等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,…...
旺店通·企业奇门与金蝶云星空对接集成订单查询连通销售订单新增(旺店通销售-金蝶销售订单-小红书)
旺店通企业奇门与金蝶云星空对接集成订单查询连通销售订单新增(旺店通销售-金蝶销售订单-小红书) 接通系统:旺店通企业奇门 慧策最先以旺店通ERP切入商家核心管理痛点——订单管理,之后围绕电商经营管理中的核心管理诉求,先后布局流量获取、会…...
卡尔曼滤波应用在数据处理方面的应用
卡尔曼滤波应用到交通领域 滤波器介绍核心思想核心公式一维卡尔曼滤波器示例导入所需的库 滤波器介绍 卡尔曼滤波器是一种用于估计系统状态的数学方法,它以卡尔曼核心思想为基础,广泛应用于估计动态系统的状态和滤除测量中的噪声。以下是卡尔曼滤波器的核…...
PROFIBUS主站转ETHERCAT协议网关
产品介绍 JM-DPM-ECT是自主研发的一款PROFIBUS-DP主站功能的通讯网关。该产品主要功能是将各种PROFIBUS-DP从站接入到ETHERCAT网络中。 本网关连接到PROFIBUS总线中作为主站使用,连接到ETHERCAT总线中作为从站使用。 产品参数 技术参数 ◆ PROFIBUS-DP/V0 协议符…...
Vue路由的使用及node.js下载安装和环境搭建
目录 一、Vue路由 1.1 简介 ( 1 ) 特点 ( 2 ) 作用 1.2 实例 ( 1 ) 引入 ( 2 ) 组件 ( 3 ) 关系 ( 4 ) 路由 ( 5 ) 事件 ( 6 ) 锚点 二、nodeJS 2.1 下载 2.2 安装 2.3 环境搭建 新增 添加 测试 配置 运行 一、Vue路由 1.1 简介 Vue路由是Vue.…...
【算法训练-二叉树 三】【最大深度与直径】求二叉树的最大深度、求二叉树的直径
废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【求二叉树的直径】,使用【二叉树】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件…...
查看linux是centos还是Ubuntu
查看linux是centos还是Ubuntu 命令:cat /etc/os-release...
win10怎么关闭自动更新,这个方法你知道吗?
Windows 10 操作系统自动更新是确保系统安全性和性能的关键功能。然而,有时用户可能希望手动控制更新,因此关闭自动更新可能是一个有用的选项。在本文中,我们将介绍win10怎么关闭自动更新的两种方法,以满足用户不同的需求。 方法1…...
「语音芯片」常见的OTP芯片故障分析
OTP语音芯片是指一次性可编程语音芯片,语音只能烧写一次,适合应用在不需要修改语音、语音长度短的场合,从放音的长度上可以分为20秒、40秒、80秒、170秒、340秒。语音芯片的特点是单芯片方案、价格便宜,适合批量生产,即便是小数量…...
孩子写作业买什么样台灯合适?适合孩子读写台灯推荐
现在孩子的普遍都存在视力问题,而导致孩子近视的原因可能跟光线太强或太弱、不用的用眼习惯、长时间的过度用眼等因素有关,根据数据表明目前中国近视患者人数达到6亿多,其中儿童青少年的视力不良率甚至高达八成,所以在孩子的学习道…...
DBAPI插件开发指南
DBAPI插件开发指南 插件市场 您可以去插件市场下载插件 插件的作用 DBAPI的插件分4类,分别是数据转换插件、缓存插件、告警插件、全局数据转化插件 缓存插件 对执行器结果进行缓存,比如SQL执行器,对查询类SQL,sql查询结果进…...
做恋爱方面的网站/seo搜索优化待遇
中国矿业大学教职工代表大会第一次会议暨工会会员中国矿业大学第八届教职工代表大会暨第十四届工会会员代表大会第一次会议代表团代表名单(以姓氏笔画为序)(共18个代表团593名代表,其中:正式代表368人、特邀代表 30人、列席代表 195 人)矿业工程学院、安…...
游戏推荐网站怎么做/一键优化表格
云开发(微信-小程序)笔记(十一)---- 分页,不简单啊 搜索的分类有单字段,多字段(或),多字段(并)搜索等 1.导入数据集 首先向数据库导入免费数据…...
电影网站怎么做友情链接/网络营销渠道的功能
\XXX表示斜扛后面的1到3位为八进制表示的 字符。 "\018"含2个字符,一个是’\01’,一个 字’8’。注意:018不是八进制,八进制没有字 符8。 "\08"含2个字符,一个是’\0’,一个 字’8’。…...
只做硬件网站/seo攻略
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼/*************************************************************************************** 外部中断0实验 *实现现象:下载程序后按下K3按键可以对D1小灯状态取反。注意事项:无。***********************…...
如何做古诗词网站/佛山网站建设方案咨询
1、打开文件 C:\Windows\System32\drivers\etc\hosts,在文件末尾加上下面代码: 127.0.0.1 www.wangdongxue.com 2、打开Apache的配置文件 httpd.conf (应该在Apache目录下的 conf 文件夹里),并在末尾加上如下代码: <virtualHos…...
简单模板网站制作时间/哪个浏览器不屏蔽网站
制作SD更新系统时和用mfgtool工具烧录时,文件如何替换? 答:制作SD更新系统时,请按照需求选择不同mfgimages-myd*文件夹。每个文件夹里面有一个Manifest文件, 里面规定了uboot, kernel, dtb,文件系统的命名规则。新编译的文件放进该…...