自定义对象作为HashMap的键,同时重写hashCode和equals方法

news2024/9/23 19:17:11

如果要将自定义类的实例 作为HashMap的 键,必须重写hashCode和equals方法

简单版本,看不懂看后面复杂版本解释

在这里插入图片描述

复杂版本解释

当我们用 HashMap存入自定义的类时,如果不重写这个自定义类的equals和hashCode方法,得到的结果会和我们预期的不一样。我们来看 WithoutHashCode.java这个例子。

在其中的第 2到第 18行,我们定义了一个 Key类;在其中的第 3行定义了唯一的一个属性 id。当前我们先注释掉第 9行的 equals方法和第 16行的 hashCode方法。
在这里插入图片描述
在 main函数里的第 22和 23行,我们定义了两个 Key对象,它们的 id都是 1,就好比它们是两把相同的都能打开同一扇门的钥匙。

在第 24行里,我们通过泛型创建了一个HashMap对象。它的键部分可以存放 Key类型的对象,值部分可以存储String类型的对象。

在第 25行里,我们通过 put方法把 k1和一串字符放入到 hm里;而在第 26行,我们想用 k2去从HashMap里得到值;这就好比我们想用 k1这把钥匙来锁门,用 k2来开门。这是符合逻辑的,但从当前结果看, 26行的返回结果不是我们想象中的那个字符串,而是 null。
原因有两个:一是没有重写hashCode方法,二是没有重写equals方法

当我们往HashMap里放 k1时,首先会调用 Key这个类的 hashCode方法计算它的 hash值,随后把 k1放入hash值所指引的内存位置。

关键是我们没有在 Key里定义 hashCode方法。这里调用的仍是 Object类的 hashCode方法(所有的类都是Object的子类),而 Object类的 hashCode方法返回的 hash值其实是 k1对象的 内存地址(假设是1000)。
在这里插入图片描述
如果我们随后是调用 hm.get(k1),那么我们会再次调用 hashCode方法(还是返回 k1的地址 1000),随后根据得到的 hash值,能很快地找到 k1。
但我们这里的代码是 hm.get(k2),当我们调用 Object类的 hashCode方法(因为 Key里没定义)计算 k2的 hash值时,其实得到的是 k2的内存地址(假设是 2000)。由于 k1和 k2是两个不同的对象,所以它们的内存地址一定不会相同,也就是说它们的 hash值一定不同,这就是我们无法用 k2的 hash值去拿 k1的原因。
当我们把第 16和 17行的 hashCode方法的注释去掉后,会发现它是返回 id属性的 hashCode值,这里 k1和 k2的 id都是1,所以它们的 hash值是相等的。
我们再来更正一下存 k1和取 k2的动作。存 k1时,是根据它 id的 hash值,假设这里是 100,把 k1对象放入到对应的位置。而取 k2时,是先计算它的 hash值(由于 k2的 id也是 1,这个值也是 100),随后到这个位置去找。
但结果会出乎我们意料:明明 100号位置已经有 k1,但第 26行的输出结果依然是 null。其原因就是没有重写 Key对象的 equals方法。

下面可能有点绕

HashMap是用链地址法来处理冲突,也就是说,在 100号位置上,有可能存在着多个用链表形式存储的对象。它们通过 hashCode方法返回的 hash值都是100。
在这里插入图片描述
当我们通过 k2的 hashCode到 100号位置查找时,确实会得到 k1。但 k1有可能仅仅是和 k2具有相同的 hash值,但未必和 k2内容相等( k1和 k2两把钥匙未必能开同一扇门),这个时候,就需要调用 Key对象的 equals方法来判断两者是否相等了。
由于我们在 Key对象里没有定义 equals方法,系统就不得不调用 Object类的 equals方法。由于 Object的固有方法是根据两个对象的内存地址来判断,所以 k1和 k2一定不会相等,这就是为什么依然在 26行通过 hm.get(k2)依然得到 null的原因。
为了解决这个问题,我们需要打开第 9到 14行 equals方法的注释。在这个equals方法里,只要两个对象都是 Key类型,且它们的 id相等,它们内容就相等。

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

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

相关文章

《现代操作系统(中文第四版)》第二章 进程与线程

第二章、进程与线程 操作系统最核心的概念就是进程,这是对正在运行程序的一个抽象。进程是操作系统提供的最古老的也是最重要的抽象概念之一,即使可以使用的cpu只有一个,他们也具有支持并发操作的能力,它们将一个单独的cpu变换成…

【azcopy】

azcopy 下载使用输出 下载 https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10#download-azcopy使用 cd /Users/YJY/Downloads/azcopy_darwin_amd64_10.19.0./azcopy copy https://tapvqacaption.blob.core.windows.net/data/save /Users/YJY/D…

多元分类预测 | Matlab 灰狼算法(GWO)优化xgboost的分类预测模型,多特征输入模型,GWO-xgboost分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab 灰狼算法(GWO)优化xgboost的分类预测模型,多特征输入模型,GWO-xgboost分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab,程序可…

【分布式应用】zabbix:代理服务器、及监控其它应用

目录 一、部署 zabbix 代理服务器1.环境配置1.2设置 zabbix 的下载源,安装 zabbix-proxy1.3部署数据库1.4在 Web 页面配置 agent 代理1.5 配置 agent 使用 proxy 二、Zabbix 监控 Windows 系统三、zabbix监控java应用3.1、客户端开启 java jmxremote 远程监控功能3.…

C语言程序设计——数据在内存中的存储

一、数据类型介绍 1.基本内置类型 char // 字符数据类型 1 B short // 短整型 2 B int // 整型 4 B long // 长整型 4 or 8 B long long //更长的整型 8 B float //单精度浮点型 4 B double //双精…

自动化漏洞挖掘方式

自动化漏洞挖掘方式 一、Goby安装使用1.1、goby简介1.2、goby下载安装1.3、简单扫描1.4、Goby插件 二、Xray安装使用2.1、XRAY简介2.2、Xray安装2.3、Xray使用2.4、爬虫模式(主动扫描)2.5、被动扫描2.6、BurpSuite联动Xray2.7、Rad联动Xray 一、Goby安装…

X、Y、Z轴上旋转角度的Eigen::Vector3d对象转换为一个旋转矩阵

#include <iostream> #include <Eigen/Core> #include <Eigen/Geometry>using namespace std; using namespace Eigen;

django框架中使用ORM设计数据库的模型

ORM关联数据的逻辑是&#xff1a; Django 中常见的模型字段类型及其含义&#xff1a; AutoField&#xff1a;一个自动递增的整型字段&#xff0c;添加记录时它会自动增长。BigAutoField&#xff1a;一个自动递增的 biginteger字段&#xff0c;添加记录时它会自动增长。CharFie…

数据结构之图

7 图的存储 &#xff08;1&#xff09;图的邻接矩阵存储 对于无向图&#xff0c;邻接矩阵第i行/列上非零元素个数是顶点vi的度。 对于有向图&#xff0c;邻接矩阵第i行上非零元素个数是顶点vi的出度&#xff0c;第i列 上非零元素个数是顶点vi的入度。 对于带权有向图有边则…

常见面试题之垃圾收回

1. 简述Java垃圾回收机制&#xff1f;&#xff08;GC是什么&#xff1f;为什么要GC&#xff1f;&#xff09; 为了让程序员更专注于代码的实现&#xff0c;而不用过多的考虑内存释放的问题&#xff0c;所以&#xff0c;在Java语言中&#xff0c;有了自动的垃圾回收机制&#x…

javaUDP数据报套接字编程

0.前言 对于UDP协议来说&#xff0c;具有无连接&#xff0c;面向数据报的特征&#xff0c;即每次都是没有建立连接&#xff0c;并且一次发送全部数 据报&#xff0c;一次接收全部的数据报。 java中使用UDP协议通信&#xff0c;主要基于 DatagramSocket 类来创建数据报套接字&a…

探索人工智能的奇妙世界:解密AI技术的未来发展

作为一名热爱技术的开发者&#xff0c;当谈到人工智能&#xff08;AI&#xff09;和焦虑商业化时&#xff0c;我总会面临一个困境&#xff1a;到底是愁眉苦脸&#xff0c;还是开怀大笑&#xff1f;让我带你走进这个有趣又争议的话题。 首先我们需要面对AI的自学能力。这些智能…

Django4.0+使用rest_framework_jwt的问题

问题描述 python版本&#xff1a;3.10 Django版本&#xff1a;4.1 djangorestframework-jwt版本&#xff1a;1.11.0 在写jwt认证功能时&#xff0c;发现run的时候会报以下错误 from django.utils.translation import ugettext as _ ImportError: cannot import name ugettext…

day69_Vue进阶

今日内容 零、 复习昨日 零、 复习昨日 nginx 静态服务器(动静分离)反向代理服务器(代理后端服务器)负载均衡异步 前端工程化 —> java代码工程 一、使用Vue-Cli搭建Vue项目 1.1 什么是vue-cli cli: Command Line 命令行工具&#xff0c;vue-cli就是vue的命令行工具&#xf…

ThreadPoolExecutor 线程池源码学习

ThreadPoolExecutor 线程池源码学习 1.阅读源码 1.ThreadPoolExecutor.execute public void execute(Runnable command) {if (command null)throw new NullPointerException();// ctl 高三位记录线程状态。低29位记录线程池中线程数int c ctl.get();//位运算获取工作线程数 …

wireshark抓包实践

目录 ifconfig ( network interfaces configuring )tcpdump 命令tcpdump&wireshark例子 ifconfig ( network interfaces configuring ) eth0表示网卡UP代表网卡开启状态RUNNING代表网卡的网线被接上mtu1500: MTU&#xff08;最大传输单元&#xff09;是指在网络中传输数据时…

【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】

volatile的作用 JMM下volatile作用 volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性”. 代码在写入 volatile 修饰的变量的时候 改变线程工作内存中volatile变量副本的值将改变后的副本的值从工作内存刷新到主内存 代码在读取 volatile 修饰的变量的时…

B067-基础环境-抽取Basegit

目录 抽取base抽取domain和querymapper接口抽取service抽取 Git优点&#xff1a;Git安装及操作Git Bash命令行操作图形化客户端TortoiseGit操作Git集成Idea操作idea会把workspace作为本地仓库gitee操作idea解决代码冲突 抽取base 抽取domain和query domain&#xff1a;所有实体…

Nodejs 依赖包的存放路径设置(按其他博客修改路径后,安装路径仍在C盘的解决办法)

Nodejs 依赖包的存放路径设置 使用命令npm root -g 查看依赖包的安装位置 默认依赖包的安装位置是在C盘。为了防止C盘存太多东西&#xff0c;我这里已经将安装位置改到了D盘&#xff0c;下面就记录下修改的步骤。 1. 创建新的依赖包安装目录 在 nodejs 的安装目录下创建两个新…

8年资深测试总结,性能测试+性能优化(详细)进军高级测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能优化常见概念…