equals、==、hashCode的区别、为什么要重写HashCode方法?

本文深入探讨了Java中equals和hashCode方法的区别与联系,解析了它们在对象比较和散列存储中的作用,以及为何在自定义类时需要重写这两个方法。

一:equals

equals用来比较的是两个对象的内容是否相等(即比较内存地址是否相同),由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象。

二:==

如果是基本类型比较的是数值大小;
如果是引用类型比较的是内存地址;

== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。

1、比较的是操作符两端的操作数是否是同一个对象。
2、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
3、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true,如:
int a=110 与 long b=110L 与 double c=110.0都是相同的(为true),因为他们都指向地址为110的堆。

代码验证实例:

public class Test {
    public static void main(String[] args) {
        String a = new String("abc"); // a 为一个引用
        String b = new String("abc"); // b为另一个引用,对象的内容一样
        String aa = "abc"; // 放在常量池中
        String bb = "abc"; // 从常量池中查找
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("a EQ b");
        if (142 == 142.1) { // true
            System.out.println("true");
        }
    }
}

PS:这里记录生产系统一个bug,就是用错了!= 导致出了系统问题

			String version = doc.get("FormVersion").toString();
			if(version  != "5"){
				drawingType = doc.get("DrawingType").toString();
				if(!"1".equals(drawingType)){
					.......
				}
			}

doc.get(“FormVersion”).toString()本身值为5,但是doc.get(“FormVersion”).toString() != "5"结果确为true,因为string类型已经对结果进行了Boolean判断,所以导致!=判断失效。
如果比较判断数值大小,这里要用equals!

代码分析

import java.util.HashSet;
import java.util.Set;

public class Test2 {
    public static void main(String[] args) {
        String s1 = new String("123");
        String s2 = new String("123");
        System.out.println(s1 == s2);//false
        System.out.println(s1.equals(s2));//true

        Set set1 = new HashSet();
        set1.add(s1);
        set1.add(s2);
        System.out.println(s1.hashCode()+"\t"+s2.hashCode());
        System.out.println(set1.size());//1
        System.out.println("**************************************************");
        Person p1 = new Person("zhangsanfeng");
        Person p2 = new Person("zhangsanfeng");
        System.out.println(p1 == p2);//false
        System.out.println(p1.equals(p2));//false
        Set set2 = new HashSet();
        set2.add(p1);
        set2.add(p2);
        System.out.println(p1.hashCode()+"\t"+p2.hashCode());
        System.out.println(set2.size());//2
    }
}

执行结果
在这里插入图片描述
结果分析
(1)String类对equals进行了重写,所以对于String类型其equals比较的是内容而不是引用地址,所以是true。而Person类属于自定义类并没有对equals进行重写,则比较的是引用地址,所以是false。
在这里插入图片描述
String重写hashcode方法,其比较的是值
在这里插入图片描述

(2)HashSet底层采用HashMap进行存储
在这里插入图片描述
HashSet的add方法,value值存储的是一个Object类型常量。底层是调用HashMap的put方法。
在这里插入图片描述
而Person类其用的是Object原生的hashcode方法,则结果不一样,所以size()为2。

三:hashCode

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
1)、如果两个对象相等,那么它们的hashCode()值一定相同。
这里的相等是指,通过equals()比较两个对象时返回true。
2)、如果两个对象hashCode()相等,它们并不一定相等。
因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等。补充说一句:“两个不同的键值对,哈希值相等”,这就是哈希冲突。

此外,在这种情况下。若要判断两个对象是否相等,除了要覆盖equals()之外,也要覆盖hashCode()函数。否则,equals()无效。

四:为什么需要重写equals?

当我们的对象之间是否相等需要根据对象的内容判断而不是通过对象存储的地址判断的时候我们就需要重写equals。
比如有一个用户类(User),里面有用户编号的属性,不管这个对象是根据什么情况创建出来的,只要他们的编号相同,我们就认为他们是相等的,这个时候就要重写equals。

五::为什么在重写equals方法的时候要重写hashcode的方法?

判断的时候先根据hashcode进行的判断,相同的情况下再根据equals()方法进行判断。如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同,而equals()方法判断出来的结果为true。

在Java中的一些容器中,不允许有两个完全相同的对象,插入的时候,如果判断相同则会进行覆盖。这时候如果只重写了equals()的方法,而不重写hashcode的方法,Object中hashcode是根据对象的存储地址转换而形成的一个哈希值。这时候就有可能因为没有重写hashcode方法,造成相同的对象散列到不同的位置而造成对象的不能覆盖的问题。

参考文章
https://blog.youkuaiyun.com/qq_28051453/article/details/52701171
https://blog.youkuaiyun.com/sssdal19995/article/details/105622577

<think>首先,用户的问题是:“我想理解在Java重写equals方法时为何需要同时重写hashCode方法 请问Java重写equals方法为什么要同时重写hashCode方法”。我需要基于系统级指令来回答。 系统级指令: - 所有行内数学表达式使用 `$...$` 格式。 - 独立公式使用 `$$...$$` 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考站内引用:用户提供了几个引用,我需要参考它们,但不是直接复制,而是基于内容整合。引用是关于为什么重写equals时必须重写hashCode的。 关键点从引用中提取: - [^1]:重写equalshashCode的基本规则,两者关系,只重写一个会导致问题。 - [^2]:hashCode默认行为产生独特值,如果不重写,对象即使相等也可能有不同hashCode。 - [^3]:equalshashCode在哈希表(如HashMap、HashSet)中相关,必须一致。 - [^4]:重写equals必须重写hashCode以确保相等的对象有相同的哈希值,否则在哈希集合中出错。 我的回答结构: 1. **介绍问题**:解释equalshashCode的作用。 2. **为什么需要同时重写**:基于哈希表的机制。 3. **潜在问题**:如果不重写hashCode,只重写equals,会发生什么。 4. **规则最佳实践**:如何正确重写两者。 5. **总结**:强调重要性。 6. **生成相关问题**:在最后添加相关问题。 使用中文回答。 添加引用标识:在相关段落末尾添加 [^1]、[^2] 等,基于提供的引用。 数学表达式:用户问题不涉及数学,但如果有数学内容,确保格式正确。这里可能不需要数学表达式,除非类比或解释概念。 逐步回答: - 步骤1:解释equals方法的作用:比较对象内容是否相等。 - 步骤2:解释hashCode方法的作用:返回对象的哈希码,用于哈希表。 - 步骤3:说明在Java中,哈希表如HashMap、HashSet依赖hashCode来存储检索对象。如果两个对象相等(equals返回true),它们必须有相同的hashCode。 - 步骤4:如果只重写equals而不重写hashCode,可能导致相等的对象有不同hashCode,违反hashCode契约,导致哈希表行为错误。 - 步骤5:给出一个简单例子来演示问题。 - 步骤6:总结规则。 生成相关问题:在最后添加2-3个相关问题。 完整回答结构: - 开头:直接回答问题。 - 主体:分点解释。 - 结尾:总结生成相关问题。</think>### Java重写equals方法时为何需要同时重写hashCode方法Java中,重写`equals`方法时必须同时重写`hashCode`方法,这是为了确保对象在哈希表(如`HashMap`、`HashSet`等)中的行为正确性。下面我将逐步解释原因,并基于Java规范相关最佳实践进行说明。 #### 1. **equalshashCode方法的作用** - **equals方法**:用于比较两个对象的内容是否相等。默认情况下(从`Object`类继承),它比较对象的内存地址(即引用相等),但重写后可以基于对象的属性(如字段值)进行比较[^3]。 - **hashCode方法**:返回一个整数哈希码,用于在哈希表中快速定位对象。默认行为基于对象的内存地址生成唯一值[^2]。 - 关键关系:Java规范要求,如果两个对象通过`equals`方法比较为相等(`obj1.equals(obj2) == true`),那么它们的哈希码必须相同(`obj1.hashCode() == obj2.hashCode()`)。这称为“hashCode契约”[^1][^4]。 #### 2. **为什么必须同时重写?** 如果不重写`hashCode`而只重写`equals`,会导致哈希表行为不一致,原因如下: - **哈希表依赖hashCode**:在`HashMap`或`HashSet`中,对象首先通过`hashCode`分配到“桶”(bucket)中,然后使用`equals`在桶内比较。如果两个对象相等但`hashCode`不同,它们会被分配到不同桶中,导致`equals`比较无法触发,从而无法正确识别相等对象[^3][^4]。 - **违反契约的风险**:只重写`equals`而不重写`hashCode`时,相等的对象可能生成不同的哈希码(因为默认`hashCode`基于内存地址)。这破坏了Java的契约规则,可能引发逻辑错误,例如对象在集合中重复存储或无法查找[^1][^2]。 - **示例说明**:假设有一个`Person`类,重写了`equals`方法基于`name`字段比较: ```java class Person { String name; // 重写equals:基于name比较 @Override public boolean equals(Object obj) { if (obj instanceof Person) { return this.name.equals(((Person) obj).name); } return false; } // 未重写hashCode } ``` 如果创建两个`name`相同的`Person`对象(`person1``person2`),`equals`返回`true`,但默认`hashCode`不同。当存入`HashSet`时: ```java Set<Person> set = new HashSet<>(); set.add(person1); set.add(person2); // 可能成功添加,因为hashCode不同导致分配到不同桶 ``` 结果:`set`大小可能为2,尽管对象内容相等。这违反了集合的唯一性要求[^4]。 #### 3. **如何正确重写两者** - **基本规则**: - 重写`equals`时,确保比较对象的所有相关属性。 - 重写`hashCode`时,必须使用与`equals`相同的属性计算哈希码,保证相等的对象产生相同的哈希值[^1]。 - 常用方法:在`hashCode`中使用`Objects.hash()`工具类,例如: ```java @Override public int hashCode() { return Objects.hash(name); // 基于name字段计算哈希码 } ``` - **最佳实践**:在IDE(如IntelliJ IDEA)中生成`equals``hashCode`方法,确保一致性。这避免了手动错误,并符合Java规范[^3][^4]。 #### 4. **总结** 重写`equals`时必须重写`hashCode`,以维护哈希表的正确性Java契约。如果不这样做,可能导致集合类(如`HashMap`)出现不可预测的行为,例如对象丢失或重复存储。始终确保两者同步,基于相同的属性实现[^1][^3][^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值