我们都知道所有的类都从Object继承了equals()和hashcode(),先来看看equals(),和hashcode()在Object中的实现:
public boolean equals(Object obj) {
return (this == obj);
}
public native int hashCode();
可以知道equals比较的实际是引用的地址,hashcode实际是一个本地方法,可以理解为返回的是
一个具体的地址。在实际使用中如果我们要使用向HashSet这类不能重复添加的集合时就必须重写equals和hashcode方法,如果你不使用集合的话,自己定义一个一般的类一般情况下是不需要重写的。为什么要重写hashcode和equals呢?hashset的内部实际是一个HashMap我们先看一下在向HashMap中添加一个元素时实际发生了什么
public V put(K key, V value) {//在此映射中关联指定值与指定键。如果该映射以前包含了一个该键的映射关系,则旧值被替换。
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
void recordAccess(HashMap<K,V> m) {
}
可以发现它首先调用key的hashcode()得到一个hash值,再调用indexFor(HashMap的底层也是用一个数组保存key和value的,有兴趣可以去看看)得到key的索引。下面将该索引对应的值赋给一个Entry(保存着key和value),e.hash == hash && ((k = e.key) == key || key.equals(k))将Entry中的key和要放入的比较,调用equals和要放入的元素比较。如果为真,用vlaue替换oldvalue,接下来记录这个值(实际重新new了一个HashMap)返oldvalue.如果为假,将HashMap长度+1,返回null。
上面分析put的过程发现,put方法内部实际上调用了对象的hashcode方法和equals方法。这就是为什么在重写equals的同时最好复写hashcode方法,如果重写equals而不重写hashcode方法会发送什么呢?在举例之前我们看看在java中,String和Integer这些预定义的包装类中已经帮我们重写了equals和hashcode:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
public int hashCode() {
int h = hash;
if (h == 0 && count > 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
可以发现String的equals实际比较的是对象的内容而不是对象引用的地址,hashcode方法是根据字符串的长度来生成hash值的。所以在实际使用的有的初学者可能会发现这样的问题:
A a = new A("hello");
A b = new A("hello");
a.equals(b);//false
String a = new String("hello");
String b = new String("hello");
a.equals(b);//true,A没有重写equals,而String帮我们重写了
回到上面的问题:假如重写equals而不重写hashcode方法会发什么?重写之后有什么变化
为了输出方便,我们这里使用HashSet(不重复的散列集)
public class EqualsAndHashcode {
public static void main(String[] args) {
HashSet h = new HashSet();
A a1 = new A(1);//不作任何重写默认使用Object的
A a2 = new A(1);// output:false false 2018699554,1311053135
System.out.println(a1 == a2);
System.out.println(a1.equals(a2));
System.out.println(a1.hashCode() + "," + a2.hashCode());
// 只复写euqals
B b1 = new B(1);//output:true,118352462,1550089733
B b2 = new B(1);// false,118352462,865113938
B b3 = new B(2);//[cn.dx.cellection.B@5c647e05
//,cn.dx.cellection.B@33909752,
//cn.dx.cellection.B@70dea4e
System.out.println(b1.equals(b2) + "," + b1.hashCode() + ","
+ b2.hashCode());
System.out.println(b1.equals(b3) + "," + b1.hashCode() + ","
+ b3.hashCode());
h.add(b1);
h.add(b2);
h.add(b3);
System.out.println(h);
// 同时重写code,和equals
C c1 = new C(1);
C c2 = new C(1);
C c3 = new C(2);
System.out.println(c1.equals(c2) + "," + c1.hashCode() + ","
+ c2.hashCode());
System.out.println(c1.equals(c3) + "," + c1.hashCode() + ","
+ c3.hashCode());
h = new HashSet();
h.add(c1);
h.add(c2);
h.add(c3);
System.out.println(h);
// true,2,2
// false,2,4
//[cn.dx.cellection.C@2, cn.dx.cellection.C@4]
}
}
class A {
private int a;
A(int i) {
this.a = i;
}
}
class B {
private int a;
B(int i) {
this.a = i;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (null == obj)
return false;
if (obj instanceof B) {
B other = (B) obj;
if (this.a == other.a)
return true;
}
return false;
}
}
class C{
private int a;
C(int i){
this.a = i;
}
@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if (null== obj)
return false;
if(obj instanceof C){
C other = (C)obj;
if(this.a == other.a)
return true;
}
return false;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return a*2<<10%10;
}
}
/**
false
false
2018699554,1311053135
true,118352462,1550089733
false,118352462,865113938
[cn.dx.cellection.B@5c647e05, cn.dx.cellection.B@33909752, cn.dx.cellection.B@70dea4e]
true,2,2
false,2,4
[cn.dx.cellection.C@2, cn.dx.cellection.C@4]
*/
从上面可以发现,如果不作任何复写默认使用Object的,只复写equals方法向hashset添加时都添加进去了,都复写不会添加重复的元素。所以,如果两个对象equals返回true,hashcode也应该是相同的,不然相同的元素也被添加进去了,违背了我们使用集合的初衷。
反之:两个元素equals返回true,hashcode不一定相等,同时都重写才相等。
hashcode相等的两个元素equals一定为true,即充分不必要。
复写equal方法应该满足:
自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
对于任何非空引用值 x,x.equals(null) 都应返回 false。
分享到:
相关推荐
### 复写`hashCode()`方法与`equals()`方法 在Java编程中,`hashCode()`方法与`equals()`方法是对象比较中的两个非常重要的方法。它们主要用于判断对象是否相等以及对象的散列值,这对于集合类(如`HashSet`)来说...
* 代码补全快捷键 Ctrl + Alt + T:生成代码(Getter、Setter、Constructor、hashCode/equals、toString) * 代码补全快捷键 Ctrl + O:复写方法 * 代码补全快捷键 Ctrl + I:实现方法 代码格式化 * 代码格式化...
Lombok 的主要目的是解决在实体 Bean 中大量的 Getter/Setter 方法,以及 toString、hashCode 等可能不会用到,但是某些时候仍然需要复写的问题。通过使用 Lombok,开发者可以方便地使用自动生成的代码,从而减少...
+ Hashtable(实现 Map 接口,同步,不允许 null 作为 key 和 value,用自定义的类当作 key 的话要复写 hashCode 和 equals 方法) + HashMap(实现 Map 接口,非同步,允许 null 作为 key 和 value,用的多) + ...
内容概要:本文档《数据结构》(02331)第一章主要介绍数据结构的基础概念,涵盖数据与数据元素的定义及其特性,详细阐述了数据结构的三大要素:逻辑结构、存储结构和数据运算。逻辑结构分为线性结构(如线性表、栈、队列)、树形结构(涉及根节点、父节点、子节点等术语)和其他结构。存储结构对比了顺序存储和链式存储的特点,包括访问方式、插入删除操作的时间复杂度以及空间分配方式,并介绍了索引存储和散列存储的概念。最后讲解了抽象数据类型(ADT)的定义及其组成部分,并探讨了算法分析中的时间复杂度计算方法。 适合人群:计算机相关专业学生或初学者,对数据结构有一定兴趣并希望系统学习其基础知识的人群。 使用场景及目标:①理解数据结构的基本概念,掌握逻辑结构和存储结构的区别与联系;②熟悉不同存储方式的特点及应用场景;③学会分析简单算法的时间复杂度,为后续深入学习打下坚实基础。 阅读建议:本章节内容较为理论化,建议结合实际案例进行理解,尤其是对于逻辑结构和存储结构的理解要深入到具体的应用场景中,同时可以尝试编写一些简单的程序来加深对抽象数据类型的认识。
内容概要:本文详细介绍了施耐德M580系列PLC的存储结构、系统硬件架构、上电写入程序及CPU冗余特性。在存储结构方面,涵盖拓扑寻址、Device DDT远程寻址以及寄存器寻址三种方式,详细解释了不同类型的寻址方法及其应用场景。系统硬件架构部分,阐述了最小系统的构建要素,包括CPU、机架和模块的选择与配置,并介绍了常见的系统拓扑结构,如简单的机架间拓扑和远程子站以太网菊花链等。上电写入程序环节,说明了通过USB和以太网两种接口进行程序下载的具体步骤,特别是针对初次下载时IP地址的设置方法。最后,CPU冗余部分重点描述了热备功能的实现机制,包括IP通讯地址配置和热备拓扑结构。 适合人群:从事工业自动化领域工作的技术人员,特别是对PLC编程及系统集成有一定了解的工程师。 使用场景及目标:①帮助工程师理解施耐德M580系列PLC的寻址机制,以便更好地进行模块配置和编程;②指导工程师完成最小系统的搭建,优化系统拓扑结构的设计;③提供详细的上电写入程序指南,确保程序下载顺利进行;④解释CPU冗余的实现方式,提高系统的稳定性和可靠性。 其他说明:文中还涉及一些特殊模块的功能介绍,如定时器事件和Modbus串口通讯模块,这些内容有助于用户深入了解M580系列PLC的高级应用。此外,附录部分提供了远程子站和热备冗余系统的实物图片,便于用户直观理解相关概念。
某型自动垂直提升仓储系统方案论证及关键零部件的设计.zip
2135D3F1EFA99CB590678658F575DB23.pdf#page=1&view=fitH
可以搜索文本内的内容,指定目录,指定文件格式,匹配大小写等
Windows 平台 Android Studio 下载与安装指南.zip
Android Studio Meerkat 2024.3.1 Patch 1(android-studio-2024.3.1.14-windows.zip)适用于Windows系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/90557033 part2: https://download.csdn.net/download/weixin_43800734/90557035
国网台区终端最新规范
国网台区终端最新规范
1.【锂电池剩余寿命预测】Transformer-GRU锂电池剩余寿命预测(Matlab完整源码和数据) 2.数据集:NASA数据集,已经处理好,B0005电池训练、B0006测试; 3.环境准备:Matlab2023b,可读性强; 4.模型描述:Transformer-GRU在各种各样的问题上表现非常出色,现在被广泛使用。 5.领域描述:近年来,随着锂离子电池的能量密度、功率密度逐渐提升,其安全性能与剩余使用寿命预测变得愈发重要。本代码实现了Transformer-GRU在该领域的应用。 6.作者介绍:机器学习之心,博客专家认证,机器学习领域创作者,2023博客之星TOP50,主做机器学习和深度学习时序、回归、分类、聚类和降维等程序设计和案例分析,文章底部有博主联系方式。从事Matlab、Python算法仿真工作8年,更多仿真源码、数据集定制私信。
Android项目原生java语言课程设计,包含LW+ppt
大学生入门前端-五子棋vue项目
这是一个完整的端到端解决方案,用于分析和预测阿联酋(UAE)地区的二手车价格。数据集包含 10,000 条二手车信息,覆盖了迪拜、阿布扎比和沙迦等城市,并提供了精确的地理位置数据。此外,项目还包括一个基于 Dash 构建的 Web 应用程序代码和一个训练好的 XGBoost 模型,帮助用户探索区域市场趋势、预测车价以及可视化地理空间洞察。 数据集内容 项目文件以压缩 ZIP 归档形式提供,包含以下内容: 数据文件: data/uae_used_cars_10k.csv:包含 10,000 条二手车记录的数据集,涵盖车辆品牌、型号、年份、里程数、发动机缸数、价格、变速箱类型、燃料类型、颜色、描述以及销售地点(如迪拜、阿布扎比、沙迦)。 模型文件: models/stacking_model.pkl:训练好的 XGBoost 模型,用于预测二手车价格。 models/scaler.pkl:用于数据预处理的缩放器。 models.py:模型相关功能的实现。 train_model.py:训练模型的脚本。 Web 应用程序文件: app.py:Dash 应用程序的主文件。 callback
资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。
资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。
此为代码审查工具 可查 文件数,字节数,总行数,代码行数,注释行数,空白行数,注释率等