equals()方法的重写
API:
java.lang
类 Object
equals
public boolean equals(Object obj)
-
指示其他某个对象是否与此对象“相等”。
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) 始终返回true
或始终返回false
,前提是对象上equals
比较中所用的信息没有被修改。 - 对于任何非空引用值
x
,x.equals(null)
都应返回false
。
Object
类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值x
和y
,当且仅当x
和y
引用同一个对象时,此方法才返回true
(x == y
具有值true
)。注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
- 自反性:对于任何非空引用值
-
-
参数:
-
obj
- 要与之比较的引用对象。
返回:
-
如果此对象与 obj 参数相同,则返回
true
;否则返回false
。
-
一.为什么需要重写equals()方法?
如API文档所说Object 类的 equals 方法实现对象上差别可能性最大的相等关系,equals()的默认行为是执行== 的比较。也就是说会去测试两个引用是否对heap上同一个对象。如果equals()没有被覆盖过,两个对象永远不会被视为相同的,因为不同的对象有不同的字节组合,默认情况下验证的是引用相等性。那么如果我们要达到对象相等性,即heap上的两个不同对象在意义上是相同的,未被覆盖的equals()方法不可能做到。比如说heap上有两个Dog对象,在逻辑上我们认定只要这两只狗品种一样,毛发颜色一样,叫声一样我们就说这两只狗是相同的,但是Object类的equals()返回结果告诉你:这两只狗不一样(这时候你恐怕要怀疑自己的三观了)。所以为了不颠覆你的认知,我们往往需要重写equals()方法,来自定义“相等”的标准。
二.怎么重写equals()方法
编写一个完美equals方法的建议:
(1)显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量。
(2)检测this与otherObject是否引用同一个对象:
if(this == othefObject) return true;
这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个地比较类中的域所付出的代价小很多。
(3)检测otherObject是否为null,如果为null,返回false。这项检测是很有必要的。
if(otherObject == null) return false;
(4)比较this与otherObject是否属于同一个类。
如果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。
return field1 == other.field1
&& Objects.equals(field2,otherfield2)
&& ...;
如果在子类中重新定义equals,就要在其中包含调用super.equals(other)。
三.关于API中的“注意”项
hashCode()与equals()的相关规定:
(1)如果两个对象相等,则hashcode必须也是相等的。
(2)如果两个对象相等,对其中一个对象调用equals()方法必须返回true。也就是说,若a.equals(b) 返回true,则b.equals(a)返回true。
(3)如果两个对象有相同的hashcode,它们也不一定相等。但若两个对象相等,则它们的hashcode值一定是相等的。
(4)因此若equals()被覆盖过,则hashCode()也必须被覆盖。
(5)hashCode()的默认行为是对在heap上的每个对象产生独特的值。如果你没有override过hashCode(),则该class的两个对象怎样都不会被认为是相同的。
也就是说:a.equals(b)必须与a.hashCode() == b.hashCode()等值
但a.hashCode() == b.hashCode()不一定与a.equals(b)等值
实现hashCode方法的目的是为了提高程序的效率,先进行hashCode的比较,如果不同就不必在进行equals的比较了。这样就大大减少了equals比较的次数,对需要比较的数量很大的程序来说效率提高是很明显的,一个很好的例子就是在集合中使用:
我们都知道java中的List集合是有序的,可以重复的;而Set集合是无序的,不能重复的,那么怎么才能保证不放入重复的元素呢?单靠equals方法比较的话,如果原来集合中已经有10000个元素,那么放入第10001个元素时,需要与前面的10000的元素进行比较,很明显这样效率是很低的,因此hashCode应运而生。java就采用了hash表,利用哈希算法,将对象数据根据对象的特征使用特定的算法将其定义到一个地址上,那么后面定义进来的数据只要看对应的hashCode地址上是否有值。于set集合而言,如果没有值,新数据将直接添加进set集合,如果有值在进行equals比较然后确定是否存在重复。如果重写了equals方法而没有重写hashCode方法,很可能出现两个equals的对象,hashcode值却不一样,这样不符合equals()和hashCode()的相关规定,而且在会造成比较结果错误:原本相等的两个对象,在hashCode比较阶段就断言它们不相等。