java重写equals方法时,为什么还需要重写hashCode

625 阅读3分钟

在说这个问题之前,先说下equals和hashCode各自的作用

equals: 判断两个对象是否相等所调用的方法。

   public boolean equals(Object obj) {
        return (this == obj);
   }

瞜一眼java的Object类的equals方法代码,很明显这个方法是比较两个对象的地址是否相等


hascode: 我也不知道这个方法有啥作用,咱们姑且算它是返回当前对象的唯一code码吧。可以理解为数据库表的主键

public native int hashCode();

这个方法是调用操作系统的本地方法,具体实现咱们不管他。

那么为啥重写了对象的equals方法后,还需要重写hashCode呢? 从Object的源码中貌似看不出来……,既然看不出来那咱们就换个位置思考,java中有哪些地方用到了这两个方法??

翻了下源码,发现java的集合框架中Map有用到这个方法。

废话不多说,咱们先瞜一眼代码

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
}

在这里插入图片描述 这段代码咱们就不说干什么的了,大家都知道的哈,类是HashMap,不过这个地方貌似有一个很熟悉的方法啊!进去咱们再瞜一眼 在这里插入图片描述 可以看到这个地方hashCode()方法被用到了,而且是被HashMap的键对象key调用了!!好吧,不废话继续撸下去

在这里插入图片描述        重点找到了,我们都知道java中HashMap的数据结构是数组+链表+红黑树,具体的详细描述可以参考blog.csdn.net/zhanglei082…        既然是这种数据结构,那么每个键值对都会被存在相应的地址中,从代码中可以看出HashMap是通过key的hashCode以及自身的容量来决定当前键值的存储索引(桶)的,确定桶的位置后,再进入桶中同时判断hashCode和equals连个方法。那也就是说,如果hashCode不同,那么HashMap就一定会创建一个新的Node键值对象。那也就会执行下面的这些代码

在这里插入图片描述 在这里插入图片描述 到此为止,结果大体上已经出来了。        HashMap在put一个键值对时,会先根据键的hashCode和equals方法来同时判断该键在容器中是否已经存在,如果存在则覆盖,反之新建。所以如果我们在重写equals方法时,没有重写hashCode方法,那么hashCode方法还是会默认使用Object提供的原始方法,而Object提供的hashCode方法返回值是不会重复的(也就是说每个对象返回的值都不一样)。所以就会导致每个对象在HashMap中都会是一个新的键。

说到这很多人又要问了,不同的对象创建一个新的键好像没有问题啊,好吧,如果有这样的疑问的话说明我的描述不是很清楚。

下面来的直观一点 ,有一个用户叫张三,我给他创建了两次

User user = new User("张三");
User user1 = new User("张三");
// 这里泛型 <用户对象,年龄>
Map<User,Integer> map = new HashMap<>();
map.put(user,20);
map.put(user1,21);

上面这段代码执行后,map中应该有几个键值对??        从代码直观上看,大家很容易会说是两个键值对,但是从业务上来说这样是不对的,不应该出现两个键值对,因为张三是同一个人,Map的值存的是他的年龄,同一个人从业务上来说是不应该在容器中出现两次的。

说到这,可能又有人会问了,Map的数据结构怎么会这么用呢,实际工作中很少有人会这么用的啊。

对的,工作中很少有人傻到将Map这么用。所以咱们换一个表现方式

Set<User> users = new HashSet<>();
users.add(new User(0001,"张三"));
users.add(new User(0001,"张三"));

大家都知道HashSet的内部实现其实就是HashMap,不过取的是HashMap的键作为Set的值。 这段代码执行的结果users的长度应该是多少呢? 不用问了,应该没有人会傻到说长度是2。

说到这咱们再贴一段代码测试下哈

public class User {

    private Long userId;

    private String name;

    public User(){}

    public User(Long userId,String name){
        this.userId = userId;
        this.name = name;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @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(userId, user.userId) &&
                Objects.equals(name, user.name);
    }
    
    public static void main(String[] args) {
        Set<User> users = new HashSet<>();
        users.add(new User(0001L,"张三"));
        users.add(new User(0001L,"张三"));
        System.out.println(users.size());
    }
}

这段代码中有一个User类,它重写了equals方法,但是没有重写hashCode方法,咱们执行下看看

在这里插入图片描述 然后我们吧hashCode()方法加上

public class User {

    private Long userId;

    private String name;

    public User(){}

    public User(Long userId,String name){
        this.userId = userId;
        this.name = name;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @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(userId, user.userId) &&
                Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userId, name);
    }

    public static void main(String[] args) {
        Set<User> users = new HashSet<>();
        users.add(new User(0001L,"张三"));
        users.add(new User(0001L,"张三"));
        System.out.println(users.size());
    }
}

在这里插入图片描述 可以看到加上了hashCode方法后,HashSet长度为1……

哎,做一个咸鱼一样的码农……