修改equals方法为什么同时需要修改hashCode方法
程序员文章站
2024-03-25 22:59:10
...
jdk注释规范说到:
1、equals相等,则hashCode必须相等(equals不相等,hashCode有可能相等)
所以,重写了equals,一般来说都需要重写hashCode方法来满足同等性。
2、如果equals相等,hashCode不相等会发生什么?
那么用到hashCode来判断相等的散列映射就会出错!!
HashTable、HashMap、HashSet等基于hashCode(哈希散列)的方法来判断对象相等,就算equals相等,如果hashCode不相等,直接被判定为不相等,两个对象在哈希列表中相等前提是hashCode相等,然后在判断equals相等。
证明:
第一种:
定义User类并都重写equals和hashCode方法(正确做法):
public class User {
public User(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
private String userName;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(userName, user.userName);
}
@Override
public int hashCode() {
return Objects.hash(userName);
}
}
用set(元素不重复)集合测试:
public static void main(String[] args) {
Set<User> sets = new HashSet<>();
sets.add(new User("1"));
sets.add(new User("1"));
sets.add(new User("1"));
for (User user : sets) {
System.out.println("key=" + user.toString() + ",value=" + user.getUserName());
}
}
结果只插入一条:
key=com.exemple.User@50,value=1
说明,即使对象不同,但是重写equals(根据userName判断)和hashCode方法后都相等了,所以无法add了。
第二种:只是重写了equals方法:
public class User {
public User(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
private String userName;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(userName, user.userName);
}
// @Override
// public int hashCode() {
// return Objects.hash(userName);
// }
}
用set(元素不重复)集合测试:
public static void main(String[] args) {
Set<User> sets = new HashSet<>();
sets.add(new User("1"));
sets.add(new User("1"));
sets.add(new User("1"));
for (User user : sets) {
System.out.println("key=" + user.toString() + ",value=" + user.getUserName());
}
}
结果,都add进去了:
key=com.exemple.User@677327b6,value=1
key=com.exemple.User@1540e19d,value=1
key=com.exemple.User@14ae5a5,value=1
说明,仅仅是equals方法相等,在使用散列集合判断相等时,仍然会判断为不相等。
原理说明:
重写hashCode方法主要是对散列集合判断是否相等有用。
因为散列集合如HashMap,经常需要判断相等,如果每次都调用equals太耗性能,所以通过散列方法来判断是否相等:
- 对于key,先通过hashCode获取散列值,如果散列值不相等,则判断这些key不相等
- 如果key相等,再调用equals方法判断最终是否相等
增加第一步的hashCode值判断的效益在于,判断键在集合中是否存在,先判断散列,而散列是有规律可循,比如直接定位到散列的内存地址,如果这里已经存在值再通过equals判断下,如果不存在则直接判断集合中不包含。
HashMap存储有一种方法是:hashCode返回的是int值,通过hashCode%arrays.length得出这个键在arrays数组中的下标,一下子就缩小了范围,剩下的一个个比较equals。
链表元素大于8个转为红黑树(jdk1.8):
综上可以猜测,如果hashCode写的不对,那么散列集合就可能出问题,比如set可以设置重复值,按键找不到值等等情况。
推荐阅读
-
为什么重写equals时还必须重写hashcode方法
-
修改equals方法为什么同时需要修改hashCode方法
-
为什么重写 equals 时必须重写 hashCode 方法?
-
修改equals方法时为什么还要重写hashcode方法?
-
HashMap中的key为什么不能为可变对象(除非重写它的hashcode方法和equals方法)
-
当集合中的对象要去重时,为什么要重写hashCode和equals方法
-
Java基础---为什么要重写hashCode和equals方法
-
讲解:为什么重写equals时必须重写hashCode方法
-
为什么重写equals方法时也要重写hashCode方法
-
为什么重写EQUALS时必须重写HASHCODE方法