【JAVA】为什么重写equals(),就必须要重写hashCode()?

本文深入探讨了在Java中重写equals()方法时为何必须重写hashCode()方法。通过解释equals与hashCode的原理,以及它们在哈希表如HashSet中的作用,揭示了遵守Java约定的重要性,以确保对象在集合中的正确行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

一、equals与hashCode到底是什么?

(1)equals()方法

查看Object的源码可知

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

Object的equals方法直接判断两个对象在内存中的地址是否相等。

(2)hashCode()方法

查看Object的源码可知

   public native int hashCode();

Object的hashCode方法是个本地方法,底层由C++或C实现的,返回对象在内存中的地址的整数形式。


二、我们为什么要用到哈希表

以HashSet集合来说,我们知道存入Set集合中的元素是不能重复的,那么HashSet是怎么判断存入的元素是否重复呢?当我们向HashSet中存入一个元素时,HashSet首先调用对象的hashCode方法,得到一个特定的哈希值,再通过哈希算法将此特定的哈希值经过运算后映射到哈希表中的一个地址上,如果这块地址上之前没有存放任何对象,HashSet就将此对象存入该块地址。如果这块地址上已经有元素存在了,HashSet就会调用对象的equals判断两个对象是否相同,如果返回true,那么此次操作失败,如果返回false,代表两个对象的哈希值虽然相同,但两者通过equals比较并不相同,那么HashSet就会将此对象散列到其他位置。

总而言之,我们重写后的equals一般是比较对象的属性是否相同,那么判断效率显著低于hashCode。好,如果java没有hashCode方法,那么某个集合在判断存入元素是否重复时,会把存入元素与之前的所有元素进行equals判断,假设之前存了10000个元素,那么仅仅执行个判断重复的操作就要执行10000次equals操作,效率低下令人汗颜。

java意识到存在这样的效率低下问题,引入了hashCode方法,可以大大提高判断重复的效率。既然引入了hashCode方法,那么大家在重写hashCode方法的时候,就需要遵守java的约定。


三、equals方法与hashCode方法的契约

Object的hashCode方法上有这样的注释

     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>

上面的大意如下

(1)每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象上的equals比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致。

(2)如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果。

(3)如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果。但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能。


四、重写equals(),就必须要重写hashCode()的理由

但我们重写了equals,拎出对象的几个重要属性进行比较,属性值不同的话返回false。如果,我们这时候不重写hashCode方法,那么hashCode依然返回对象的内存地址的整数形式。就会出现这样的情况,两个对象equals返回true,但hashCode返回的整数并不相同,破坏了源码中对重写hashCode提出的规则,因此,我们重写equals,就必须要重写hashCode。

 

 

 

 

 

 

 

<think>首先,用户的问题是关于Java重写equals方法时为何必须同时重写hashCode方法。我需要基于提供的引用内容来构建回答。 引用内容总结: - 引用[1]:重写equals时必须重写hashCode,以确保对象在哈希集合(如HashMap、HashSet)中正确使用。 - 引用[2]:如果不重写hashCode,可能会导致equals相同的对象但hashCode不同,这在哈希集合中会引起问题。 - 引用[3]:两个对象equals返回true时,hashCode也必须相同;这是Java规范的要求。 - 引用[4]:重写equals时必须重写hashCode,以确保相等的对象有相同的哈希值,以便在哈希集合中正常工作。 系统指令: - 所有行内数学表达式使用$...$格式,但这个问题是关于Java编程的,可能没有数学内容,所以我可以忽略数学部分。 - 独立公式使用$$...$$格式并单独成段,同样,可能不需要。 - LaTeX语法正确:确保任何公式正确。 - 使用中文回答:整个回答必须是中文。 - 生成相关问题:在回答结束后,生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识:如[^1],[^2]等。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 用户可见层指令: - 回答结构清晰,逐步解决问题。 - 尽量保证回答真实可靠。 我的回答结构: 1. **引言**:解释问题的重要性。 2. **原因解释**:逐步说明为什么必须同时重写hashCode。 3. **关键点**:基于引用,解释equalshashCode的关系。 4. **后果**:如果不重写hashCode会发生什么。 5. **如何正确实现**:简要说明如何重写hashCode。 6. **总结**。 7. **相关问题**:生成相关问题。 生成相关问题: - 基于主题,生成2-3个相关问题。 最终回答格式: - 使用中文。 - 引用标识:当引用特定内容时添加[^n]。 - 代码块:如果需要展示代码,使用代码块格式。 由于这个问题是概念性的,不需要代码,但可以引用示例。 内容大纲: 1. **介绍equalshashCode方法**: - equals方法用于比较对象内容是否相等。 - hashCode方法返回对象的哈希码。 - 在Java中,两者有契约关系。 2. **为什么必须同时重写**: - Java规范要求:如果两个对象equals返回true,则它们的hashCode必须相同[^3]。 - 确保在哈希集合中正常工作:如HashMap、HashSet等依赖于hashCode来存储和查找对象[^1][^4]。 - 如果不重写hashCode,可能导致equals相同的对象但hashCode不同,这违反了契约,并可能导致对象在集合中丢失或重复[^2]。 3. **后果示例**: - 例如,在HashSet中,添加两个equals相同但hashCode不同的对象时,它们可能被存储在不同的桶中,导致集合中有重复元素[^4]。 4. **如何正确重写**: - 重写hashCode时,确保equals相同的对象有相同的hashCode。 - 常用方法:使用对象的字段来计算hashCode,例如使用Objects.hash()方法。 5. **总结**:重写equals时必须重写hashCode以维护Java的契约和确保集合类的正确行为。 相关问题: 1. 如何正确重写equalshashCode方法? 2. 在Java中,hashCode方法的具体实现有哪些最佳实践? 3. 如果不重写hashCode方法,会对程序性能产生什么影响? 确保回答真实可靠:基于提供的引用,这些引用来自可靠来源(如Java文档或常见面试题)。</think>### Java重写equals方法必须同时重写hashCode方法的原因 在Java中,重写`equals`方法时**必须**同时重写`hashCode`方法,这是Java语言规范的核心要求。这一规则确保了对象在哈希集合(如`HashMap`、`HashSet`或`Hashtable`)中能正确工作,并维护了对象相等性的一致性。下面我将逐步解释原因,帮助您彻底理解这一问题。 #### 1. **equalshashCode方法的契约关系** - Java规定了一个严格的契约:如果两个对象通过`equals`方法比较返回`true`,则它们调用`hashCode`方法返回的哈希码**必须相同**[^3]。反之则不一定成立(哈希码相同的对象不一定equals相同)。 - 这一契约是Java集合框架的基础。例如,`HashMap`在存储键值对时,先根据键的`hashCode`确定存储位置(桶),再使用`equals`精确比较键是否相等。如果契约被破坏,集合的行为将不可预测[^1][^4]。 #### 2. **如果不重写hashCode的后果** - **哈希集合失效**:假设只重写了`equals`而未重写`hashCode`,可能导致两个`equals`相同的对象拥有不同的哈希码。在`HashSet`中添加这样的对象时,它们可能被分配到不同的桶中,导致集合中出现“重复”元素(违反集合的唯一性)[^2][^4]。 - 示例:定义一个`Person`类,只重写`equals`(基于姓名和年龄),但不重写`hashCode`。如果将两个`equals`相同的`Person`对象添加到`HashSet`,它们可能都被存储(因为哈希码不同),而理论上应被视为同一对象。 - **性能问题**:在`HashMap`中查找键时,先通过`hashCode`快速定位桶,再用`equals`确认。如果哈希码不一致,查找可能退化到线性搜索(时间复杂度从$O(1)$变为$O(n)$),严重影响性能[^1]。 #### 3. **为什么哈希集合依赖这一契约** - 哈希集合的设计优化了存储和检索效率。`hashCode`提供了对象的“快速摘要”,而`equals`确保精确相等。如果只依赖`equals`,集合需要遍历所有元素比较,效率极低;而`hashCode`将对象分组到桶中,大幅减少比较次数[^1][^4]。因此,维护契约是保证集合类正确性和高效性的前提[^3]。 #### 4. **如何正确重写hashCode** - 重写`hashCode`时,需确保:如果`a.equals(b) == true`,则`a.hashCode() == b.hashCode()`。常用方法是基于对象的相同字段(用于`equals`比较的字段)计算哈希码。 - 示例实现(使用Java的`Objects.hash`工具类): ```java public class Person { private String name; private int 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 && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); // 基于name和age字段计算哈希码 } } ``` - 这里,`Objects.hash`自动生成一致的哈希码,确保契约被遵守[^2][^4]。 #### 总结 重写`equals`时必须重写`hashCode`,主要是为了维护Java的对象相等性契约,确保哈希集合类(如`HashMap`)能正确、高效地工作。如果忽略这一规则,程序可能出现逻辑错误(如集合中重复元素)或性能下降。在面向对象设计中,这被视为最佳实践,也是Java面试中的高频考点[^1][^3][^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SunAlwaysOnline

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值