【Java】对象为啥要用hashCode方法 (数智边界科技)

引言:

1.图书馆如何快速找到《Java编程思想》这本书 ?
2.相同的两本《Java编程思想》书,如何找到它 ?

核心比喻:图书馆和索书号

想象一个巨大的图书馆(这就是 HashMapHashSet)。

  1. 没有 hashCode 的情况(只用 equals):

    • 你要找一本叫《Java编程思想》的书。

    • 管理员只能从第一排书架开始,一本一本地拿起来看封面,对比书名是否等于《Java编程思想》。

    • 直到找到为止。效率极低,这是 O(n) 的时间复杂度。

  2. 有 hashCode 的情况:

    • 你要找同一本书。

    • 这次,书籍都有一个 索书号(这就是 hashCode),比如 747.5。这个索书号是通过一个特定算法(哈希函数)根据书名、作者等信息计算出来的。

    • 管理员直接根据 747.5 这个索书号,走到标有 747 的书架区,然后很快在那一小堆书里找到编号为 .5 的那本。

    • 效率极高,因为管理员直接定位到了大致区域,只需要在极少数书籍(一个“桶”或“篮子”里)中进行精细比较。这接近 O(1) 的时间复杂度。

结论:hashCode 的核心目的就是为了“快速定位”,它是给哈希表这种数据结构使用的“索引”或“地址”。


为什么 hashCode 必须和 equals 一起工作?

现在我们来解释那个著名的“契约”。继续用图书馆的例子:

  • hashCode 契约第一条:如果两本书的索书号不同,它们肯定不是同一本书。

    • 这很合理,书都放在不同的架子上,当然不是同一本。

  • hashCode 契约第二条(最关键):如果两本书是同一本书(equals 为 true),它们的索书号必须相同。

    • 想象一下,图书馆里有两本完全相同的《Java编程思想》(假设它们是相等的)。如果一本的索书号是 747.5,另一本是 328.1,会发生什么?

    • 你用《Java编程思想》计算出的索书号是 747.5,然后去 747 书架找,只找到了其中一本。另一本你永远也找不到,因为它被放在了 328 书架! 这彻底破坏了哈希表的逻辑。

精细比较(equals)成本很高,所以先用“快速定位”(hashCode)缩小范围。定位到同一个“桶”之后,再用 equals 进行最终确认。


一个会出错的代码示例

public class Student {
    private String id;
    private String name;
​
    // 假设我们只重写了 equals,认为id和name相同就是同一个学生
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) && Objects.equals(name, student.name);
    }
​
    // 但是忘记了重写 hashCode()
    // 那么将使用从Object类继承的默认实现,该实现通常根据内存地址生成一个不同的整数。
}
​
public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("001", "Alice");
        Student s2 = new Student("001", "Alice"); // 在逻辑上,s1 等于 s2
​
        HashMap<Student, String> map = new HashMap<>();
        map.put(s1, "优秀");
​
        // 现在尝试用“相等”的s2对象去取值
        String grade = map.get(s2); 
        System.out.println(grade); // 输出:null !!!
    }
}

为什么会输出 null?

  1. map.put(s1, "优秀"):HashMap 调用 s1.hashCode()(默认实现)得到一个值,比如 12345。它将 <s1, "优秀"> 这个键值对存放在编号为 12345 的桶里。

  2. map.get(s2):HashMap 调用 s2.hashCode()(默认实现)得到另一个值,比如 67890。因为 67890 != 12345,HashMap 会直接去 67890 这个桶里找,而那个桶是空的!它根本不会调用 equals 方法去比较 s1s2

修复方法:Student 类中同时重写 hashCode

java

@Override
public int hashCode() {
    // 使用和equals方法相同的字段来计算哈希码
    return Objects.hash(id, name);
}

现在,s1s2hashCode() 返回值相同了。当 map.get(s2) 时,HashMap 会定位到正确的桶,然后在那个桶里调用 equals 方法进行精确比较,最终成功找到 s1 对应的值 "优秀"

总结

  1. 为什么要有 hashCode?

    • 为了速度。它让基于哈希的集合(如 HashMap, HashSet) 能够通过一个整数快速定位到数据的可能存储位置,避免遍历整个集合。

  2. 为什么 hashCode 必须和 equals 一致?

    • 为了保证哈希表的正确性。如果两个相等的对象拥有不同的哈希码,它们就会被放入哈希表的不同位置,导致你永远无法用另一个“相等”的对象找到它。这破坏了哈希表最根本的查找功能。

简单说:hashCode 是用来快速找“大概在哪”,equals 是用来最终确认“是不是它”。两者必须配合,才能既快又准。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值