hashCode和 equals你用对了么?

你好,我是吴计可师,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。

点击下方👇关注公众号,带你一起复习后端技术,看看面试考点,补充积累技术知识,每天都为面试准备积累

文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读


在 Java 中,hashCode() 和 equals() 方法对于对象的比较、存储和查找至关重要。它们的主要作用是确保对象在集合中(特别是 Map 和 Set)能够按照我们预期的方式正确地比较和存储。


01
hashCode() 方法的作用


hashCode() 方法返回一个整数,用于对象的哈希值。哈希值通常用于哈希表(例如 HashMap 和 HashSet)中定位对象的存储位置。哈希值将对象映射到某个特定的桶或位置,从而使得哈希表能够在平均 O(1) 的时间复杂度内进行查找、插入和删除操作。

hashCode() 的重要性

  • 哈希表中的定位:在 HashMap 和 HashSet 等集合类中,hashCode() 用于决定对象在哈希表中的存储位置。如果两个对象具有相同的哈希值,它们将被存储在同一个桶中(发生哈希冲突),然后再通过 equals() 方法进行比较来判断它们是否相等。

  • 性能优化:好的 hashCode() 实现能够均匀地将对象分布到哈希表的各个桶中,减少哈希冲突,提高集合操作的性能。

hashCode() 方法的要求

  • 一致性:如果两个对象通过 equals() 比较是相等的,那么它们的 hashCode() 必须相同。也就是说,equals() 相等的对象必须具有相同的哈希码。

  • 不必相等:即使两个对象的 hashCode() 不相同,也不意味着它们通过 equals() 方法不相等。不同的 hashCode() 值可以指向不同的对象,但相同的 hashCode() 值则可能需要进一步通过 equals() 来比较。

  • 冲突处理:不同对象可以有相同的哈希值(即哈希冲突),但这不会影响它们通过 equals() 方法判断是否相等。


02
equals() 方法的作用


equals() 方法用于比较两个对象是否相等。它是一个逻辑上的比较,不仅仅是比较对象的引用(即内存地址)。通常情况下,当两个对象通过 equals() 方法被认为相等时,我们认为它们是具有相同值或状态的对象。

equals() 的重要性:

  • 逻辑比较:equals() 方法用于在对象比较时定义何为“相等”。例如,在 HashMap 中,如果两个对象作为键存储,它们是否“相等”由 equals() 决定。

  • 防止误判:equals() 方法能够防止两个对象因为引用不同而被误判为不同的对象(即便它们的内容相同)。如果 equals() 返回 true,表示这两个对象在逻辑上是等价的。

equals() 方法的要求:

  • 自反性:一个对象应该与其自身相等,即 x.equals(x) 应该返回 true。

  • 对称性:如果 x.equals(y) 返回 true,则 y.equals(x) 也应该返回 true。

  • 传递性:如果 x.equals(y) 返回 true 且 y.equals(z) 返回 true,则 x.equals(z) 也应该返回 true。

  • 一致性:如果 x.equals(y) 返回 true,则在同一运行期间,多次调用 x.equals(y) 应该始终返回 true。

  • 非空性:对于任何非空的对象 x,x.equals(null) 应该返回 false。


03
hashCode() 和 equals() 的关系


一致性:hashCode() 和 equals() 必须保持一致性。这意味着如果两个对象通过 equals() 被认为是相等的,那么它们的 hashCode() 必须相等。如果两个对象的 hashCode() 不相同,它们通过 equals() 比较肯定是不相等的。

class Person {    private String name;    private int age;    public Person(String name, int age) {        this.name = name;        this.age = age;    }    @Override    public boolean equals(Object obj) {        if (this == obj) return true;        if (obj == null || getClass() != obj.getClass()) return false;        Person person = (Person) obj;        return age == person.age && name.equals(person.name);    }    @Override    public int hashCode() {        return Objects.hash(name, age);    }}

在这个例子中,Person 类重写了 equals() 和 hashCode(),确保了两个 Person 对象如果在 name 和 age 上相等,它们的 hashCode() 也相同。


hashCode() 决定哈希表中的位置,equals() 确定是否相等:在 HashMap 或 HashSet 中,当我们向集合中添加一个元素时,首先通过 hashCode() 计算出元素的位置。如果发生哈希冲突(即两个对象具有相同的 hashCode()),就会调用 equals() 方法进一步比较这两个对象是否相等。如果 equals() 返回 true,就认为这两个对象是相等的。


04
hashCode() 和 equals() 在集合类中的应用


HashMap 和 HashSet:这两个集合类依赖 hashCode() 来定位对象的位置,如果 hashCode() 相同,则通过 equals() 进一步比较对象是否相等。

例如,在 HashMap 中,键值对的存储和查找是基于键的哈希值的。如果两个键的 hashCode() 相同,它们会被存储在相同的桶中,然后通过 equals() 判断这两个键是否相等。

TreeMap 和 TreeSet:这些集合类依赖 compareTo() 方法(对于自然排序)或 Comparator(对于自定义排序)来排序元素,而不是 hashCode() 和 equals()。

ConcurrentHashMap:在并发环境下,hashCode() 和 equals() 的正确性依然至关重要,因为它们用于确定对象的位置和相等性。


05
总结


  • hashCode() 用于快速定位对象在哈希表中的位置,并且它的实现直接影响到集合的性能。

  • equals() 用于逻辑比较,确保两个对象在逻辑上是否相等。

  • 在使用 HashMap、HashSet 等基于哈希表的数据结构时,必须正确地实现 hashCode() 和 equals(),以确保集合操作的正确性和高效性。

  • 如果一个类是作为集合中的键或元素使用,必须保证 hashCode() 和 equals() 一致,并遵循它们的合同。


今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!

END


扫码关注

一起积累后端知识
不积跬步,无以至千里
不积小流,无以成江海

喜欢此内容的人还喜欢

谈谈id那些事(五)——美团的 Leaf 的ID生成


一个阿里二面面试官必问的问题


谈谈id那些事(三)——阿里巴巴的 TDDL的ID生成


分享面试:mysql数据库索引失效的情况


面试常被忽略的问题——内存区域划分