Java中equals方法和hashcode方法有什么不同?Object类是什么?

前言

在看Java面试题的时候遇到的题目,hashCode()和equals()的区别。我只知道equals方法却不知道hashCode()是啥。因此翻阅资料开始学习。

一、Object类

在了解hashCode方法前,还应该搞懂Object类。

Object类是Java中所有类的始祖,在Java中每个类都是由它扩展而来的。因此Object类被称为Java中所有类的超类,也叫作父类。如果没有明确指出超类,Object就被认为是这个类的超类。

可以使用Object类型的变量引用任何类型的对象:

Object obj = new Employee("Radish", 10000);

注意,Object类型的变量只能用于作为各种值的通用持有者。要想对其中的内容进行具体操作,还需清楚对象的原始类型,并进行相应的类型转换:

Employee e = (Employee)obj;

另外,只有基本类型不是对象,例如,数值、字符和布尔类型的值都不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object类

二、equals方法

Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。然而对于多数类来说,这种判断并没有什么意义。然而,经常需要检测两个对象状态的相等性,如果两个对象的状态相等,就认为这两个对象是相等的

因此,针对不同的类,应该重写出对应的符合规范的equals方法。

例如,如果两个雇员对象的姓名、薪水和雇用日期都一样,就认为它们是相等的,利用下面这个实例演示equals方法的实现机制。

在这里插入图片描述
注意,为了防备name或hireDay可能为null的情况,需要使用Object.equals方法。如果两个参数都为null,Objects.equals(a,b)调用将返回true;如果其中一个参数为null,则返回false;否则,如果两个参数都不为null,则调用a.equals(b)。

因此
在这里插入图片描述
在子类中定义equals方法时,首先调用超类的equals。如果检测失败,对象就不可能相等。

Java语言规范要求equals方法具有下面的特性:

  • 自反性:对于任何非空引用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引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。
  • 对于任意非空引用x,x.equals(null)应该返回false。

编写一个完美的equals方法的建议:

  • 显示参数命名为otherObject,稍后需要将它转换称为另一个叫做other的变量。
  • 检测this与otherObject是否引用同一个对象:
if(this == otherObject) return true;
  • 检测otherObject是否为null,如果为null,返回false。这项检测很必要的。
if(otherObject == null) return false;
  • 比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:
if(getClass() != otherObject.getClass) return false;
  • 将otherObject转换为相应的类类型变量:
ClassName other = (ClassName)otherObject;
  • 现在开始对所有需要比较的域进行比较了。使用 == 比较基本类型域,使用equals比较对象域
return field1 == other.field1 
	&& Objects.equals(field2,other.field2)
	&& ...;

三、hashCode方法

散列码(hashCode) 是由对象导出的一个整型值。散列码是没有规律的。如果x和y是两个不同的对象,x.hashCode() 与 y.hashCode() 基本上不会相同。

由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。例子:

String s = "OK";
StringBuilder sb = new StringBuilder(s);
System.out.println(s.hashCode() + " " + sb.hashCode());
String t = new String("OK");
StringBuilder tb = new StringBuilder(t);
System.out.println(t.hashCode() + " " + tb.hashCode());

运行结果:
在这里插入图片描述
解析: 字符串s与t拥有相同的散列码,这是因为字符串的散列码是由内容导出的。而字符串缓冲sb与tb却有着不同的散列码,这是因为在StringBuffer类中没有定义hashCode方法,它的散列码是由Object类的默认hashCode方法导出的对象存储地址

如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。
hashCode方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均与。

例如,下面是Employee类的hashCode方法:
在这里插入图片描述
不过还可以做的更好。首先,最好使用null安全的方法Object.hashCode。如果是参数为null,这个方法会返回0,否则返回对参数调用hashCode的结果。另外使用静态方法Double.hashCode来避免Double对象

在这里插入图片描述
还有更好的做法,需要组合多个散列值时,可以调用Objects.hash并提供多个参数。这个方法会对各个参数调用Objects.hashCode,并组合这些散列值。
在这里插入图片描述

四、hashCode()和equals的区别

  • 从性能方面看,重写的equals()里一般比较全面和复杂,这样效率就会比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。
  • 从可靠性看,hashCode()并不是完全可靠的,有时候不同的对象他们生成的hashcode也会一样(生成hash值的公式可能存在问题),所以hashCode()只能说是大部分时候可靠。

因此,两个重要的结论

  • equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals对比是绝对可靠的。
  • hashCode()相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。

参考文献

《Java核心技术 卷1 基础知识 第10版》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值