为什么重写 equals() 时必须重写 hashCode()?

本文详细介绍了Java中equals()方法的工作原理及其与'=='操作符的区别。探讨了equals()方法的重写规则,并给出了最佳实践建议。

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

equals() 的所属以及内部原理(即 Object 中 equals 方法的实现原理)

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

实际上我们知道所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较两个对象的的内存地址,所以说 Object 的 equals() 方法是比较两个对象的内存地址是否相等,即若 object1.equals(object2) 为 true,则表示 equals1 和 equals2 实际上是引用同一个对象。

equals() 与 ‘==’ 的区别

总结:默认情况下也就是从超类 Object 继承而来的 equals 方法与 ‘==’ 是完全等价的,比较的都是对象的内存地址,但我们可以重写 equals 方法,使其按照我们的需求的方式进行比较,如 String 类重写了 equals 方法,使其比较的是字符的序列,而不再是内存地址。

equals()的重写规则

  • 自反性。对于任何非null的引用值xx.equals(x)应返回true

  • 对称性。对于任何非null的引用值xy,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true

  • 传递性。对于任何非null的引用值xyz,如果y.equals(x)返回truey.equals(z)返回true,那么x.equals(z)也应返回true

  • 一致性。对于任何非null的引用值xy,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false

  • 对于任何非空引用值xx.equal(null)应返回false

当然在通常情况下,如果只是进行同一个类两个对象的相等比较,一般都可以满足以上5点要求。

有父类的时候需要注意覆写父类的 equals 方法:

 	@Override
	public boolean equals(Object obj) {
		if (obj instanceof BigCar) {
			BigCar bc = (BigCar) obj;
			return super.equals(bc) && count == bc.count;
		}
		return super.equals(obj);
	}

为什么重写 equal() 时也得重写 hashCode()

这个问题主要是针对映射相关的操作(Map接口) Map接口的类会使用到键对象的哈希码,当我们调用put方法或者get方法对Map容器进行操作时,都是根据键对象的哈希码来计算存储位置的,因此如果我们对哈希码的获取没有相关保证,就可能会得不到预期的结果。

Objects.hashCode(o)Objects.hash(...o)

Java 7 中对hashCode方法做了两个改进,首先java发布者希望我们使用更加安全的调用方式来返回散列码,也就是使用null安全的方法 Objects.hashCode(注意不是Object而是java.util.Objects) 方法,这个方法的优点是如果参数为null,就只返回0,否则返回对象参数调用的hashCode的结果。

Objects.hashCode 源码如下:

public static int hashCode(Object o) {
    return o != null ? o.hashCode() : 0;
}

Java 7 还提供了另外一个方法 java.util.Objects.hash(Object... objects), 当我们需要组合多个散列值时可以调用该方法。

@Override
public int hashCode() {
	return Objects.hash(name,salary,sex);
}

在 Java API 文档中关于 hashCode 方法有以下几点规定(原文来自java深入解析一书)。

  • 在java应用程序执行期间,如果在 equals 方法比较中所用的信息没有被修改,那么在同一个对象上多次调用 hashCode 方法时必须一致地返回相同的整数。如果多次执行同一个应用时,不要求该整数必须相同。

  • 如果两个对象通过调用 equals 方法是相等的,那么这两个对象调用 hashCode 方法必须返回相同的整数。

  • 如果两个对象通过调用 equals 方法是不相等的,不要求这两个对象调用 hashCode 方法必须返回不同的整数。 但是程序员应该意识到对不同的对象产生不同的hash值可以提供哈希表的性能。

编写一个完美equals()的几点建议

(出自Java核心技术 第一卷:基础知识)

  • (1)显式参数命名为 otherObject, 稍后需要将它转换成另一个叫做 other 的变量

  • (2)检测 thisotherObject 是否引用同一个对象 :if(this == otherObject) return true;(存储地址相同,肯定是同个对象,直接返回true)

  • (3) 检测 otherObject 是否为null ,如果为null,返回falseif(otherObject == null) return false;

  • (4) 比较 thisotherObject 是否属于同一个类 (视需求而选择)

    • 如果equals的语义在每个子类中有所改变,就使用 getClass 检测 : if(getClass()!=otherObject.getClass()) return false

    • 如果所有的子类都拥有统一的语义,就使用 instanceof 检测 :if(!(otherObject instanceof ClassName)) return false

  • (5) 将 otherObject 转换为相应的类类型变量:ClassName other = (ClassName) otherObject ;

  • (6) 现在开始对所有需要比较的域进行比较 。使用 == 比较基本类型域,使用 equals 比较对象域。如果所有的域都匹配,就返回true,否则就返回false

如果在子类中重新定义 equals,就要在其中包含调用super.equals(other)

当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定: 相等对象必须具有相等的哈希码

关于 list.contains(o) 方法: 其实内部是for循环拿 o.equals(a[i]) 去比较的

@Override         
public boolean contains(Object o) {   
     return indexOf(o) != -1;   
}  
@Override  
public int indexOf(Object o) {  
    E[] a = this.a;  
	// 当o是null的时候也会返回集合中null的位置
	if (o == null) {  
	    for (int i = 0; i < a.length; i++)  
	        if (a[i] == null)  
	            return i;  
	} else {  
		// indexOf(),实际调用的是传入的contains(Object o)的实参的equals方法
	    for (int i = 0; i < a.length; i++)  
     	   if (o.equals(a[i]))  
	            return i;  
	}  
	return -1;   
}

参考:https://blog.youkuaiyun.com/javazejian/article/details/51348320

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

川峰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值