在Java學(xué)習(xí)中霍狰,hashCode和equals方法灿巧,是一個繞不開的話題浮定。
這兩個方法到底有什么作用扇苞?這兩個方法什么時候需要重寫?如何重寫橡娄?
這篇文章诗箍,我們一起捋一捋Java中的hashCode和equals方法。
hashCode和equals是Object的方法
我們可以參考查閱JDK8的官方API文檔:https://docs.oracle.com/javase/8/docs/api/index.html
Object類的描述
Class Object是類層次結(jié)構(gòu)的根挽唉。每個類都有Object作為超類扳还。所有對象(包括數(shù)組)都實(shí)現(xiàn)此類的方法。
所有的Java類默認(rèn)繼承Object類橱夭。只是隱式地默認(rèn)集成。而沒有使用extends關(guān)鍵字桑逝。
hashCode方法描述
返回對象的哈希碼值棘劣。
提供此方法是因?yàn)楣1淼膬?yōu)勢,例如HashMap中使用的哈希表楞遏。
hashCode的一般契約是:
- 每當(dāng)在執(zhí)行Java應(yīng)用程序期間多次在同一對象上調(diào)用它時茬暇,hashCode方法必須始終返回相同的整數(shù),前提是不修改對象的equals比較中使用的信息寡喝。從應(yīng)用程序的一次執(zhí)行到同一應(yīng)用程序的另一次執(zhí)行糙俗,該整數(shù)不需要保持一致。
解讀:
一次執(zhí)行中的多次調(diào)用预鬓,同一個對象需要返回同一個hashcode值巧骚。
多次執(zhí)行同一個對象,每次返回的hashcode值可以不一樣格二。
- 如果兩個對象根據(jù)equals(Object)方法相等劈彪,則對兩個對象中的每一個調(diào)用hashCode方法必須生成相同的整數(shù)結(jié)果。
- 如果兩個對象根據(jù)equals(java.lang.Object)方法不相等顶猜,則不需要在兩個對象中的每一個上調(diào)用hashCode方法必須生成不同的整數(shù)結(jié)果沧奴。但是,程序員應(yīng)該知道长窄,為不等的對象生成不同的整數(shù)結(jié)果可能會提高散列表的性能滔吠。
盡可能合理,Object類定義的hashCode方法確實(shí)為不同的對象返回不同的整數(shù)挠日。(這通常通過將對象的內(nèi)部地址轉(zhuǎn)換為整數(shù)來實(shí)現(xiàn)疮绷,但Java?編程語言不需要此實(shí)現(xiàn)技術(shù)。)
equals方法描述
表明某個其他對象是否“等于”此對象肆资。
equals方法在非null對象引用上實(shí)現(xiàn)等價關(guān)系:
- 自反性:對于任何非空引用值x矗愧,x.equals(x)應(yīng)該返回true。
- 對稱性:對于任何非空引用值x和y,當(dāng)且僅當(dāng)y.equals(x)返回true時唉韭,x.equals(y)才應(yīng)返回true夜涕。
- 傳遞性:對于任何非空引用值x,y和z属愤,如果x.equals(y)返回true而y.equals(z)返回true女器,則x.equals(z)應(yīng)返回true。
- 一致性:對于任何非空引用值x和y住诸,x.equals(y)的多次調(diào)用始終返回true或始終返回false驾胆,前提是不修改在對象的equals比較中使用的信息。對于任何非空引用值x贱呐,x.equals(null)應(yīng)返回false丧诺。
類Object的equals方法實(shí)現(xiàn)了對象上最具辨別力的等價關(guān)系;也就是說,對于任何非空引用值x和y奄薇,當(dāng)且僅當(dāng)x和y引用同一對象時驳阎,此方法才返回true(x == y的值為true)。
請注意馁蒂,通常需要在重寫此方法時覆蓋hashCode方法呵晚,以便維護(hù)hashCode方法的常規(guī)協(xié)定,該方法聲明相等的對象必須具有相等的哈希代碼沫屡。
針對上述官方描述饵隙,我們繼續(xù)查看equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
我們追求的是業(yè)務(wù)上的內(nèi)容相等
查看上述源碼可知,在Object類中沮脖,equals比較的是對象地址是否相等金矛,即是否是同一個對象,而不是比較值是否相等勺届。
而我們業(yè)務(wù)中通常追求的是只是業(yè)務(wù)上的內(nèi)容相等绷柒,所以我們業(yè)務(wù)中使用的類都是需要重寫equals和hashCode方法的。
重寫equals方法涮因,同時必須重寫hashCode方法
我們從Objec類官方API文檔中废睦,可知:
“如果兩個對象根據(jù)equals(Object)方法相等,則對兩個對象中的每一個調(diào)用hashCode方法必須生成相同的整數(shù)結(jié)果”养泡。
所以我們重寫equals方法的時候嗜湃,為了滿足這個原則,也必須同時重寫hashCode方法澜掩。
public class ObjectEquals {
public static void main(String[] args) {
Student student1 = new Student("LeBron Jame", 12);
Student student2 = new Student("LeBron Jame", 12);
System.out.println(student1.equals(student2));
}
private static class Student {
String name;
Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
}
}
上述示例中购披,返回結(jié)果為false,因?yàn)镾tudent類并沒有重寫equals方法肩榕,而是直接使用Object類中的equals方法刚陡,直接用==比較兩個對象指向的地址是否一致惩妇,即是否是同一個對象,上述兩個對象只是內(nèi)容相同筐乳,但不是同一個對象歌殃,所以結(jié)果返回false。
public class ObjectEquals {
public static void main(String[] args) {
Student student1 = new Student("LeBron Jame", 12);
Student student2 = new Student("LeBron Jame", 12);
System.out.println(student1.equals(student2));
}
private static class Student {
String name;
Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
Student student = (Student) obj;
return (name.equals(student.name) && age.equals(student.age));
}
}
}
上述示例蝙云,重寫Objec的equlas方法之后氓皱,只比較兩個對象的內(nèi)容是否相同,所以結(jié)果返回true勃刨。這就是業(yè)務(wù)上的內(nèi)容相等波材。
System.out.println("對象student1的哈希值:"+student1.hashCode());
System.out.println("對象student2的哈希值:"+student2.hashCode());
上述示例中,我們增加打印兩個對象的hashCode值身隐,運(yùn)行結(jié)果為:
對象student1的哈希值:1735600054
對象student2的哈希值:21685669
所以廷区,如果我們只重寫equals方法,但是不重寫hashCode方法的話贾铝,兩個對象的hashCode不一致躲因。
@Override
public int hashCode() {
return name.hashCode() * 31 + age;
}
上述示例,我們重寫hashCode方法忌傻,運(yùn)行結(jié)果:
對象student1的哈希值:147570189
對象student2的哈希值:147570189
可知,兩個對象的hashCode值一致了搞监。
總結(jié)
- 重寫equals方法的時候水孩,必須同步重寫hashCode方法。
- 兩個對象equals相等琐驴,hashCode值必然相等俘种。
- 兩個對象hashCode值相等,equals不一定相等绝淡,因?yàn)榭赡艽嬖诠_突宙刘。
- hashCode方法,主要用于Java中的集合類牢酵,通過hashCode值進(jìn)行高效率查找悬包。
PS
- 強(qiáng)烈推薦學(xué)習(xí)Java多看官方API文檔,結(jié)合官方API文檔看源碼馍乙,追本溯源布近。
- 強(qiáng)烈推薦閱讀《Effective Java》,非常經(jīng)典的一本好書丝格。