为什么重写 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方法也一起重写了!!!

posted @ 2022-11-24 22:59  金鳞踏雨  阅读(20)  评论(0编辑  收藏  举报  来源