为什么重写equals方法必须也要重写hashCode方法
目录
一、了解equals方法与hashCode方法
1、equals方法
2、hashCode方法
hashCode方法
hash值
3、分析
二、分析
1、equals在观感上的问题
2、只重写equals带来的问题
三、总结
一、了解equals方法与hashCode方法
1、equals方法
java顶级父类Object的equals方法源码
public boolean equals(Object obj) {
return (this == obj);
}
可以看出equals方法底层通过”==“判断是否相等,即判断两个对象的地址是否相同
2、hashCode方法
hashCode方法
hashcode方法通过特定的算法,将对象的内存地址等所有信息映射为一个数组,即散列值
hash值
系统通过特定的算法,将散列值映射为一个定长的值,即hash值(哈希值)
hash值主要是用来在散列存储结构中确定对象的存储地址的,提高对象的查询效率
3、分析
如果两个对象通过equals判断相等,那么它们的地址和内容都一定相等,因此算出来的hashCode和hash值也一定相等
如果两个对象通过equals判断不相等,那么它们的地址一定不同,但是内容可能相等,算出来的hashCode和hash值有可能相等
二、分析
1、equals在观感上的问题
使用原生equals是通过两个对象的地址判断是否相等,这会导致一个问题,两个对象拥有完全相同的内容,但是equals判断不相等
private String name;
private Integer age;
public test0830(String name, Integer age) {
this.name = name;
this.age = age;
}
test0830 t1 = new test0830("张三",20);
test0830 t2 = new test0830("张三",20);
System.out.println(t1.equals(t2));
在人的主观感受上,内容相同的两个对象是应该相等的,即使他们地址不同
因此可以重写equals方法,重写后只要内容完全相同,那么就返回true。但这会带来新的问题。
@Override
public boolean equals(Object obj){
if (this == obj){
return true;
}
if (obj == null || getClass() != obj.getClass()){
return false;
}
test0830 temp = (test0830)obj;
return this.name == temp.name && this.age ==temp.age;
}
2、只重写equals带来的问题
在重写equals方法后,上面t1和t2两个对象经过判断是相等的
但是这两个对象的地址不同,他们的hash值也不同,当把这两个对象放进HashSet中,会有两个值,而不是去重后只有一个值
HashSet<test0830> set = new HashSet<>();
set.add(t1);
set.add(t2);
System.out.println(set.size());
为了解决这种问题,需要重写hashCode方法,让hashCode根据name和age来映射散列表,不再管内存地址
@Override
public int hashCode(){
return Objects.hash(name,age);
}
这下HashSet实现了去重,问题解决
三、总结
根据人对”相等“的主观认识而重写的equals方法,如果不再根据对象的地址判断是否相等,HashSet就会出现不能去重的问题。因为此时hashCode方法仍然根据对象的内存地址生成不同的散列值,最终确定对象地址的hash值也会不同,影响HashSet中数据的存储。
HashSet
- 用于存放对象
- HashSet 是一个没有重复元素的集合。
- 基于HashMap实现
HashMap
- 存放键值对,key-value
- key不可重复,value可重复
- 底层基于数组+链表+红黑树实现,红黑树需要jdk≥1.8。数组的每一项都是个单链表,每个键值对根据key的hash算法确定数组中的存放位置,再根据equals判断在链表上的位置。只重写equals,会造成equals判断为true的多个对象hash值不同,而存储在不同位置,相当于不重复,出现程序逻辑错误