重写equals()方法的注意事项及其原因,为什么重写equals()时需要hashCode()方法?

本文深入探讨了在Java中重写equals和hashCode方法的重要性。详细解释了equals方法的等价关系属性,以及重写equals时为何同时需要重写hashCode方法。通过示例展示了如何正确实现这两个方法,以确保在集合类如HashMap和HashSet中的正确行为。

为什么要重写equals()方法?

因为默认equals在比较两个对象时,是看他们是否指向同一个地址的。但有时,我们需要两个不同对象只要是某些属性相同就认为它们equals()的结果为true。比如:

person p1 = new person(1,"name");
person p2 = new person(1,"name");
如果不重写equals的话,他们是不相同的,所以我们要重些equals,判断只要他们的id和名字相同equals就为true,在一些集合里有时也这样用,集合里的contain也是用equals来比较

 

Object关于equals()的源码:

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

重写需要注意的属性:

equals方法实现了一个等价关系(equivalence relation)。它有以下这些属性:
• 自反性:对于任何非空引用x,x.equals(x)必须返回true。
• 对称性:对于任何非空引用x和y,如果且仅当y.equals(x)返回true时x.equals(y)必须返回true。
• 传递性:对于任何非空引用x、y、z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)必须返回true。
• 一致性:对于任何非空引用x和y,如果在equals比较中使用的信息没有修改,则x.equals(y)的多次调用必须始终返回true或始终返回false。
• 非空性: 对于任何非空引用x,x.equals(null)必须返回false。
当重写equals方法时,同时也要重写hashCode方法。

重写equals()的例子:

public final class Person {

    private final short height, weight;

    public PhoneNumber(short heightnum, short weightnum) {
        this.heightnum= height;
        this.weightnum= weight;
       
    }


    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Person))
            return false;
        if (o == null)
            return false;

        Person p = (Person) o;

        return p.height == this.height && p.weight == this.weight;
    }

}

注:重写equals()很多时候需要重写hashCode()!

 

首先先弄清楚hashCode()是用来干嘛的?

一提到hashcode,很自然就想到哈希表。将某一key值映射到表中的一个位置,从而达到以O(1)的时间复杂度来查询该key值。Object类源码(如下所示)中,hashCode()是一个native方法,哈希值的计算利用的是内存地址。

 

Object中hashcode()的源码:

public native int hashCode();

上面使用了native关键字,说明这是一个本地方法。使用了native关键字的方法就说明这个方法是一个调用Java本地接口(Java Native Interface,即JNI)实现的方法。

 

什么是JNI(Java本地接口)?

简单点说,就是用其他编程语言,主要是C或C++或汇编语言实现的接口。

通过JNI,Java可以调用一些由其他语言开发的功能,或者可以被其他的语言所调用,从而实现了Java和其他语言的双向连接。

 

什么时候需要重写hashCode()方法?

在每个类中,在重写 equals 方法的时侯,一定要重写 hashcode 方法。如果不这样做,你的类违反了hashCode的通用约定,这会阻止它在HashMap和HashSet这样的集合中正常工作。

 

hashCode()方法和equals()方法的关系

1. 如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同!!!!; 
2. 如果两个对象不同(即用equals比较返回false),那么它们的hashCode值可能相同也可能不同; 
3. 如果两个对象的hashCode相同(存在哈希冲突),那么它们可能相同也可能不同(即equals比较可能是false也可能是true) 
4. 如果两个对象的hashCode不同,那么他们肯定不同(即用equals比较返回false)
 

为什么重写equals()就要重写hashCode()方法?

对于对象集合的判重,如果一个集合含有10000个对象实例,仅仅使用equals()方法的话,那么对于一个对象判重就需要比较10000次,随着集合规模的增大,时间开销是很大的。但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的hashCode不相同,也不再需要调用equals()方法,从而大大减少了equals()比较次数。 
所以从程序实现原理上来讲的话,既需要equals()方法,也需要hashCode()方法。那么既然重写了equals(),那么也要重写hashCode()方法,以保证两者之间的配合关系。
 

仅仅重写了equals(),而没有重写hashCode()方法,会出现什么情况? 

字段属性值完全相同的两个对象因为hashCode不同,所以在hashmap中的table数组的下标不同,从而这两个对象就会同时存在于集合中,所以重写equals()就一定要重写hashCode()方法。

但是应该是有个前提,就是你需要用到HashMap,HashSet等Java集合。用不到哈希表的话,其实仅仅重写equals()方法也可以吧。而工作中的场景是常常用到Java集合,所以Java官方建议 重写equals()就一定要重写hashCode()方法。 
 

 

<think> 我们正在讨论Java中重写equals方法hashCode方法关系。用户已经明确提问:"重写equals方法必须同重写hashCode方法原因"。根据之前的引用内容,我们需要系统地解释这个原因。 核心原因:为了维护Java中对象在哈希集合(如HashMap、HashSet等)中行为的一致性,必须保证当两个对象通过equals方法判断相等,它们的hashCode也必须相等。 我们将从以下几个方面展开: 1. equalshashCode的契约(contract)要求 2. 哈希集合(如HashMap、HashSet)的工作原理 3. 违反契约导致的后果 4. 重写hashCode的原则 注意:根据用户要求,我们需要在回答中自然地添加引用标识,引用内容来自用户提供的引用[1]到[4](但用户引用的内容实际上是关于同一个问题的不同表述)。由于用户提供的引用都是关于这个主题,我们可以统一引用为[^1](因为都指向同一个核心原因),但为了准确,我们可以将多个引用合并标注。 然而,在用户提供的引用中,引用[1]、[2]、[3]、[4]都是关于为什么重写equals必须重写hashCode的,所以我们可以这样标注: [^1] 引用自用户提供的引用[1]和[4]的内容,强调必须保证相等的对象具有相同的哈希值。 [^2] 引用自用户提供的引用[2]和[3],强调哈希集合依赖hashCodeequals。 但我们也可以统一用一个引用标识,因为所有引用都指向同一个原因。这里为了简洁,我们统一使用[^1]来指代用户提供的所有引用,因为所有引用都支持这一观点。 但是,根据用户要求,引用的标注要自然。因此,我们可以在关键点处标注引用。 此外,用户要求使用中文,数学公式用$...$或$$...$$。 由于这个主题不涉及数学公式,所以我们主要用文字描述。 结构: 1. 引言:说明Object类中equalshashCode的默认行为 2. 为什么需要重写equals(回顾):因为需要内容比较 3. 哈希集合的依赖:重点解释hashCode在哈希集合中的作用 4. 契约要求:如果不重写hashCode会导致什么问题 5. 后果举例:用HashMap的例子说明 6. 重写hashCode的原则 7. 结论 同,生成相关问题。 注意:用户要求回答结构清晰,逐步解决问题。 </think> ### 为什么重写equals必须同重写hashCode方法的详细解析 在Java中,重写`equals`方法必须同重写`hashCode`方法是一项**强制性规范**,原因主要基于以下核心原则: #### 1. **哈希集合的底层依赖** Java集合框架(如`HashMap`, `HashSet`, `HashTable`)依赖`hashCode`定位数据存储位置: - **存储过程**: 1. 调用对象的`hashCode()`计算桶位索引:$index = \text{hashCode} \% \text{tableSize}$ 2. 在桶内链表/红黑树中使用`equals()`精确匹配对象 - **查找过程**: 若两个对象`equals`为`true`但`hashCode`不同,会存储在不同桶位,导致`contains()`等方法失效:[^1][^2][^4] ```java Map<Student, Integer> map = new HashMap<>(); map.put(new Student("101", "Alice"), 95); // 未重写hashCode:即使ID相同也返回null System.out.println(map.get(new Student("101", "Alice"))); // null ❌ ``` #### 2. **对象一致性契约** Java规范要求对象必须满足: $$ \text{若 } obj1.equals(obj2) = true \text{,则 } \\ obj1.hashCode() = obj2.hashCode() $$ 违反此契约将导致以下问题:[^3] - 哈希集合行为不可预测(如重复插入"相等"对象) - `HashSet`可能包含重复元素 - 违反`Object.hashCode()`的通用约定(JLS
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值