为什么重写equals()就必须重写hashCode()

equals()hashCode()方法是java.lang.Object类中的两个方法,所有Java类都会继承这两个方法。
本文解释了equals()函数和hashCode()函数的作用,以及它们之间的关系,为什么重写equals()就必须重写hashCode()函数。

equals()

大部分情况,我们自己定义的类会重写equals()方法,比如:

class People{
    private String name;

    People() {}

    People(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        } else {
            People p = (People)o;
            return p.name.equals(this.name);
        }
    }
}

这时,如果我们创建两个name一样的People对象调用equals()方法判断它们是否相等,就会返回true。

public class Main {
    public static void main(String[] args) {
        People p1 = new People("Bob");
        People p2 = new People("Bob");
        System.out.println(p1.equals(p2));
    }
}
true

hashCode()

Object中的hashCode()方法是native方法,也就是用C或者C++语言实现的,该方法通常用来将对象的内存地址转换为整数之后返回,返回的整数称为哈希码

举个例子:

public class Main {
    public static void main(String[] args) {
        People p1 = new People("Bob");
        People p2 = new People("John");
        System.out.println(p1 + " -> " + p1.hashCode());
        System.out.println(p2 + " -> " + p2.hashCode());
    }
}
People@776ec8df -> 2003749087
People@16b98e56 -> 381259350

当然这里的People@776ec8df并不是真正的内存地址,只是个标识。

学过数据结构哈希表的同学都知道,大部分情况下,不同的输入哈希函数会计算出不同的值,少数情况下,不同的输入哈希函数也可能得到相同的值;另外,相同的输入哈希函数计算出的值一定相同。也就是说,不同的对象调用hashCode()少数情况下可能得到相同的哈希码

equals()与hashCode()的关系

我们平时会使用类似HashSetHashMap这样的数据结构来编写程序,那么在插入一个数据的时候它们是如何判断数据是否相等的?这里以HashSet为例。

插入一个对象时,HashSet会先调用对象的hashCode()函数计算哈希码,如果哈希码与所有已加入集合的对象都不同,那么新加入的对象一定与原有对象不同,直接加入集合中;如果哈希码与某个已加入集合的对象哈希码相同,这两个对象也可能并不相同,再调用equals()来判断它们是不是真的相同,不同再加入集合。

这种实现方式大大减少了equals()函数的调用次数,提高了执行速度。

为什么重写equals()就必须重写hashCode()

打开Java官方文档查看java.lang.Object.hashCode()方法的说明

请添加图片描述

在官方文档中硬性规定了:

  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

即:如果两个对象调用equals()方法返回true,那么这两个对象的hashCode()方法必须返回值相同的整数值。

我们依然以上述提到的People类为例,我们已经实现了equals()方法,但是并没有实现hashCode()方法,就会出现这种情况:两个对象调用equals()函数相等,但是它们的hashCode()并不相等。

public class Main {
    public static void main(String[] args) {
        People p1 = new People("Bob");
        People p2 = new People("Bob");
        System.out.println(p1.equals(p2));
        System.out.println(p1.hashCode() == p2.hashCode());
    }
}
true
false

所以我们必须重写hashCode()函数使得它们也相等。这是笔者随意给出一种重写方式:

    @Override
    public int hashCode() {
        return name.hashCode();
    }

再次执行Main类中的main()方法,结果就满足了官方的规定:

true
true

那么问题又来了,为什么官方要这样规定?

本文前部分已经解释了hashCode()函数的作用,相同的输入哈希函数计算出的值一定相同。既然我们重写了equals()方法认为这两个地址不同的对象逻辑上相同,那么两个对象计算出的哈希码也应该是相同的。如果我们不重写hashCode(),那么这两个逻辑上相同的对象在插入类似HashSet这样的数据结构中时,因为它们的地址不同,HashSet会认为它们是不同的对象而重复插入,这不是我们想要的结果,因为我们认为它们是相同的。

例如,两个People对象,但它们其实是同一个人Bob,如果不重写hashCode()函数,HashSet就会重复插入造成无法预料的后果。

People p1 = new People("Bob");
People p2 = new People("Bob");

Reference

IT无知君的博客 - CSDN博客

JavaGuide - GitHub

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值