欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

修改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太耗性能,所以通过散列方法来判断是否相等:

  1. 对于key,先通过hashCode获取散列值,如果散列值不相等,则判断这些key不相等
  2. 如果key相等,再调用equals方法判断最终是否相等
    增加第一步的hashCode值判断的效益在于,判断键在集合中是否存在,先判断散列,而散列是有规律可循,比如直接定位到散列的内存地址,如果这里已经存在值再通过equals判断下,如果不存在则直接判断集合中不包含。

HashMap存储有一种方法是:hashCode返回的是int值,通过hashCode%arrays.length得出这个键在arrays数组中的下标,一下子就缩小了范围,剩下的一个个比较equals。
修改equals方法为什么同时需要修改hashCode方法
链表元素大于8个转为红黑树(jdk1.8):

修改equals方法为什么同时需要修改hashCode方法


综上可以猜测,如果hashCode写的不对,那么散列集合就可能出问题,比如set可以设置重复值,按键找不到值等等情况。