为什么重写 equals 还要重写 hashCode 方法?
关于equals与hashCode关系的描述
我们可以先来看一下这个定理
(1)如果两个对象的 hashCode 值相等的情况下,对象的内容值不一定相等(hash碰撞问题)
(2)如果使用 equals 方法比较两个对象内容值相等的情况下,则两个对象的hashCode值一定要相等
相信有看过类似文章的小伙伴都可以回忆起一些什么来!
我们可以用代码的形式来解释上述问题
public class Demo {
public static void main(String[] args) {
String a1 = "a";
Integer a2 = 97;
System.out.println(a1.hashCode()); //97
System.out.println(a2.hashCode()); //97
//false
System.out.println(a1.equals(a2));
}
}
可以看出 a1 和 a2 的hashCode值是一样的;但是,使用equals比较内容值是不一样的!就也就印证了定理的第一个小点!
这也是发生Hash碰撞的原因!!!
再看看下面的例子
public class Demo {
public static void main(String[] args) {
UserEntity user1 = new UserEntity("Harmony", 1234);
UserEntity user2 = new UserEntity("Harmony", 1234);
System.out.println(user1 == user2); // false
System.out.println(user1.equals(user2)); // false
System.out.println(user1.hashCode()); // 1163157884
System.out.println(user2.hashCode()); // 1956725890
}
}
这里使用 “==” 来判断对象,结果一定是false,因为user1和user2不是同一个对象,它们的内存地址是不同,使用 “==” 比较的就是内存地址!!!
为什么我们两个对象里面的内容值是一样的,而equals值却不相等呢???
判断equals() 的结果是false,两个hashCode的也是不相同的!
我们可以按住ctrl然后点击上面的equals()和hashCode()方法跟进看一下,发现它们都是调用Object类的方法,如下
// Object 类
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
而第一个样例代码,跟进看发现,它们是调String类和Integer类的 hashCode() 和 equals() 方法,显然在String类和Integer类中对这两个方法做了重写!!!
已String类为例
// hashCode
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
// equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
显然,这样子就很好解释了。这是因为我们没有对自己创建的UserEntity对象重写equals()和hashCode()方法!!!
UserEntity类
public class UserEntity {
private String userName;
private Integer userId;
public UserEntity(String userName, Integer userId) {
this.userName = userName;
this.userId = userId;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
UserEntity user = (UserEntity) obj;
return user.userName.equals(userName) && user.userId.equals(userId);
}
@Override
public int hashCode() {
return userName.hashCode() + userId.hashCode();
}
}
重新运行,结果如下:
为什么要一起重写?
(1)Set 正常使用
Set 集合是用来保存不同对象的,相同的对象就会被 Set 合并,最终留下一份独一无二的数据。
Set 集合最大的特点:去重!
但是当我们的Set集合里面存的是自定义对象呢?比如上述的UserEntity
Set<UserEntity> set = new HashSet<>();
显然,这样子让set失去了意义,在HashSet底层也是用hashCode来判断是否是同一个元素!
阿里巴巴Java开发手册 —— equals与hashCode问题
在阿里巴巴的Java开发手册中提到了如果我们要重写 equals 方法,就一定要把hashCode方法也一起重写了!!!