equals和hashcode
java当中所有的类都继承于Object这个基类,在object中的基类定义了一个equals方法,public
boolean
equals(Object obj) {
return
(
this
== obj);
}
这个方法的初始行为是比较引用,但在一些类库中这个方法被覆盖掉了,如String,Integer,Date等在这些类中equals有其自身的实现,而不再是比较对象在栈内存中的地址(即引用)了,如果不覆盖,则equals默认行为是比较引用。对于引用数据类型之间进行equals比较,在没有覆盖equals方法的情况下,他们之间的比较还是基于对象的引用,因为object的equals方法也是用==进行比较的,所有比较后的结果与双等号的结果相同。
如果我们希望equals比较的不再是引用,我们就需要覆写equals方法。
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
。
举一个覆写了equals的例子:
1 class Strudent{ 2 private int age; 3 private String name; 4 ......//构造函数以及setter和getter 5 public boolean equals(Object obj){ 6 boolean result=false; 7 if(obj==null){//若传入的对象为空 8 result=false; 9 } 10 if(this==obj){//若传入的对象和Student对象的引用相同,则表示指向了同一个堆内存中的对象 11 result=true; 12 } 13 if(obj instanceof Student){//obj对象是否是Student类的实例 14 Student stu=(Student)obj;//将Object类向下转型为Student类 15 if(stu.getName().equals(this.name)&&stu.getAge()==this.age)//这里的equals方法调用的是String类库中已被覆写过的equals方法,因getName()返回的String类型的值 16 result=true; 17 }else{ 18 result= false; 19 } 20 return result; 21 } 22 }
注意:当equals方法被覆写时,通常有必要覆写hashCode方法,以维护hashCode方法的常规规定,该协定声明相等的对象必须具有相等的哈希码。
上面代码中的hashCode()方法可以这样写:public int hashCode(){ return (this.name.hashCode()+this.age)*31;}
在覆写了equals方法的类中,必须也覆写hashCode方法,如果不覆盖“键”的hashCode()和equals(),那么使用散列的数据结构(HashSet, HashMap, LinkedHashSet, LinkedHashMap)就无法正确地处理你的“键”。
hashCode()这个方法也是在object类中定义的:public native int hashCode();
hashCode()是一个本地方法,它的实现与本地机器相关。hashCode()生成对象的哈希码值,它默认是使用对象的地址计算出的哈希码,返回的是整数类型,如果没有覆写hashCode()方法,任何对象的哈希码值都是不相等的。而设计hashCode的最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该生成同样的值。如果在将一个对象用put()添加进HashMap时产生一个hashCode()值,而在用get()取出时却产生了另一个hashCode()值,那么就无法重新取得该对象了。所以,如果你的hashCode()方法依赖于对象中易变的数据,那么当数据变化时,hashCode就会生成一个不同的哈希码,相当于产生了一个不同的键。也不应该使hashCode()依赖于具有唯一性的对象信息,尤其是使用this的值,这只能产生很糟糕的hashCode(),因为这样做无法生成一个新的键,使之与put()中原始的键值对中的键相同。
要想使hashCode()实用,它的速度必须快,而且必须有意义。也就是说,它必须基于对象的内容生成哈希码。哈希码不必是独一无二的(应该更关注生成速度,而不是唯一性),但是通过hashCode()和equals(),必须能够完全确定对象的身份。
hashCode()的返回值(哈希码值)和equals()的关系如下:
如果x.equals(y)返回“true",那么x和y的hashCode()必须相等。
如果x.equals(y)返回”false",那么x和y的hashCode()有可能相等,也有可能不相等。
还有注意:String、Integer、Boolean、Double等这些类都覆写了equals和hashCode方法,这个两个方法是根据对象的内容来比较和计算哈希码值的。所以,主要对象的基本类型值相同,那么哈希码值就一定相同。