Java中如何判斷兩個對象是否相等

如何判斷兩個對象相等,這個問題實際上可以看做是如何對equals方法和hashcode方法的理解。

從以下幾個點來理解equals和hashCode方法:

1、equals的作用及與==的區(qū)別。

2猖凛、hashcode的作用及與equals的關(guān)系。

1绪穆、equals的作用及與==的區(qū)別辨泳。

equals被用來判斷兩個對象是否相等。

equals通常用來比較兩個對象的內(nèi)容是否相等玖院,==用來比較兩個對象的地址是否相等菠红。

equals方法默認(rèn)等同于“==”

Object類中的equals方法定義為判斷兩個對象的地址是否相等(可以理解成是否是同一個對象),地址相等則認(rèn)為是對象相等司恳。這也就意味著途乃,我們新建的所有類如果沒有復(fù)寫equals方法绍傲,那么判斷兩個對象是否相等時就等同于“==”扔傅,也就是兩個對象的地址是否相等。

Object類中equals的方法實現(xiàn)如下:



public?boolean?equals(Object?obj) {

? ? ? ? return?(this?==?obj);

}


但在我們的實際開發(fā)中烫饼,通常會認(rèn)為兩個對象的內(nèi)容相等時猎塞,則兩個對象相等,equals返回true杠纵。對象內(nèi)容不同荠耽,則返回false。

所以可以總結(jié)為兩種情況

1比藻、類未復(fù)寫equals方法铝量,則使用equals方法比較兩個對象時,相當(dāng)于==比較银亲,即兩個對象的地址是否相等慢叨。地址相等,返回true务蝠,地址不相等拍谐,返回false。

2、類復(fù)寫equals方法轩拨,比較兩個對象時践瓷,則走復(fù)寫之后的判斷方式。通常亡蓉,我們會將equals復(fù)寫成:當(dāng)兩個對象內(nèi)容相同時晕翠,則equals返回true,內(nèi)容不同時寸宵,返回false崖面。

舉個例子:



public class EqualTest {

public static void main(String[] args) {

Person p1 =new Person(10,"張三");

? ? ? ? Person p2 =new Person(10,"張三");

? ? ? ? System.out.println(p1.equals(p2));

? ? }

}

class Person{

int age;

? ? Stringname;

? ? public Person(int age, String name) {

super();

? ? ? ? this.age = age;

? ? ? ? this.name = name;

? ? }

public int getAge() {

return age;

? ? }

public void setAge(int age) {

this.age = age;

? ? }

public StringgetName() {

return name;

? ? }

public void setName(String name) {

this.name = name;

? ? }

}


Person未復(fù)寫equals方法,則默認(rèn)使用了Object中的equals梯影,即為兩個對象(p1和p2)的內(nèi)存地址判斷巫员,p1和p2很明顯內(nèi)存地址不同,所以輸出結(jié)果很明顯為false甲棍。

如果我們復(fù)寫equals方法呢简识?我們認(rèn)為名字和年齡一樣的就是同一個人,那么p1和p2都表示10歲的張三感猛,這兩個對象應(yīng)該是相等的七扰。復(fù)寫的equals方法如下:



@Override

public boolean equals(Object obj) {

if (this == obj)

return true;

? ? if (obj ==null)

return false;

? ? if (getClass() != obj.getClass())

return false;

? ? Person other = (Person) obj;

? ? if (age != other.age)

return false;

? ? if (name ==null) {

if (other.name !=null)

return false;

? ? }else if (!name.equals(other.name))

return false;

return true;

}


同樣的,執(zhí)行上述用例陪白,得到的結(jié)果是true颈走。

BTW:如果equals方法返回true,那么==是否也是true咱士?

不一定是true立由。equals返回true有兩種可能,一種是兩個對象地址相同序厉,一種是兩個對象內(nèi)容相同锐膜。當(dāng)內(nèi)容相同時,地址可能不同弛房,所以==比較的結(jié)果可能為false道盏。

我們把main方法加上對==的判斷,如下:


public static void main(String[] args) {

Person p1 =new Person(10,"張三");

? ? Person p2 =new Person(10,"張三");

? ? System.out.println(p1.equals(p2));

? ? System.out.println(p1 == p2);

}


輸出結(jié)果很明顯 p1==p2的結(jié)果是false文捶。

補充Java中對Equals的要求:



1.?對稱性:如果x.equals(y)返回是"true"荷逞,那么y.equals(x)也應(yīng)該返回是"true"。

2.?反射性:x.equals(x)必須返回是"true"粹排。

3.?類推性:如果x.equals(y)返回是"true"种远,而且y.equals(z)返回是"true",那么z.equals(x)也應(yīng)該返回是"true"恨搓。

4.?一致性:如果x.equals(y)返回是"true"院促,只要x和y內(nèi)容一直不變筏养,不管你重復(fù)x.equals(y)多少次,返回都是"true"常拓。

5.?非空性渐溶,x.equals(null),永遠(yuǎn)返回是"false"弄抬;x.equals(和x不同類型的對象)永遠(yuǎn)返回是"false"茎辐。


2、hashCode的作用及與equals的關(guān)系掂恕。

hashCode的作用是用來獲取哈希碼拖陆,也可以稱作散列碼。實際返回值為一個int型數(shù)據(jù)懊亡。用于確定對象在哈希表中的位置依啰。

Object中有hashcode方法,也就意味著所有的類都有hashCode方法店枣。

但是速警,hashcode只有在創(chuàng)建某個類的散列表的時候才有用,需要根據(jù)hashcode值確認(rèn)對象在散列表中的位置鸯两,但在其他情況下沒用闷旧。

java中本質(zhì)上是散列表的類常見的有HashMap,HashSet钧唐,HashTable

所以忙灼,如果一個對象一定不會在散列表中使用,那么是沒有必要復(fù)寫hashCode方法的钝侠。但一般情況下我們還是會復(fù)寫hashCode方法该园,因為誰能保證這個對象不會出現(xiàn)再hashMap等中呢?

舉個例子:

兩個對象equals相等的時候机错,hashcode并不一定相等爬范。


public class EqualTest {

public static void main(String[] args) {

Person p1 =new Person(10, "張三");

? ? ? ? Person p2 =new Person(10, "張三");

? ? ? ? System.out.println(

"p1.equals(p2)=" + p1.equals(p2) +", p1.hashcode=" + p1.hashCode() +", p2.hashcode=" + p2.hashCode());

? ? }

}

class Person {

? ? int age;

? ? String name;

? ? public Person(int age, String name) {

????????super();

? ? ? ? this.age = age;

? ? ? ? this.name = name;

? ? }

public int getAge() {

return age;

? ? }

public void setAge(int age) {

this.age = age;

? ? }

public StringgetName() {

return name;

? ? }

public void setName(String name) {

this.name = name;

? ? }

@Override

publicboolean equals(Object obj) {

if (this == obj)

return true;

?if (obj ==null)

return false;

if (getClass() != obj.getClass())

return false;

? ? ? ? Person other = (Person) obj;

? ? ? ? if (age != other.age)

return false;

? ? ? ? if (name ==null) {

if (other.name !=null)

return false;

? ? ? ? }else if (!name.equals(other.name))

return false;

return true;

? ? }

}


Person沒有復(fù)寫hashCode方法父腕,使用Object默認(rèn)的hashCode實現(xiàn)弱匪,輸出結(jié)果如下:

p1.equals(p2)=true, p1.hashcode=246688959, p2.hashcode=1457895203

從結(jié)果可以看出,equals雖然相同璧亮,但是p1和p2的hashcode并不相同萧诫。

如果Person用于散列表的類中呢,這里用HashSet來做測試枝嘶。


public class EqualTest {

public static void main(String[] args) {

Person p1 =new Person(10, "張三");

? ? ? ? Person p2 =new Person(10, "張三");

? ? ? ? System.out.println("p1.equals(p2)=" + p1.equals(p2) +", p1.hashcode=" + p1.hashCode() +", p2.hashcode=" + p2.hashCode());

? ? ? ? HashSet set =new HashSet();

? ? ? ? set.add(p1);

? ? ? ? set.add(p2);

? ? ? ? System.out.println(set);

? ? }

}

class Person {

int age;

? ? String name;

? ? public Person(int age, String name) {

super();

? ? ? ? this.age = age;

? ? ? ? this.name = name;

? ? }

public int getAge() {

return age;

? ? }

public void setAge(int age) {

this.age = age;

? ? }

public StringgetName() {

return name;

? ? }

public void setName(String name) {

this.name = name;

? ? }

@Overridepublicboolean equals(Object obj) {

if (this == obj)

return true;

? ? ? ? if (obj ==null)

return false;

? ? ? ? if (getClass() != obj.getClass())

return false;

? ? ? ? Person other = (Person) obj;

? ? ? ? if (age != other.age)

return false;

? ? ? ? if (name ==null) {

if (other.name !=null)

return false;

? ? ? ? }else if (!name.equals(other.name))

return false;

return true;

? ? }

@Overridepublic StringtoString() {

return "Person [age=" +age +", name=" +name +"]";

? ? }

}


輸出結(jié)果

p1.equals(p2)=true, p1.hashcode=246688959, p2.hashcode=1457895203

[Person [age=10, name=張三], Person [age=10, name=張三]]

p1和p2的equals相同帘饶,我們認(rèn)為是兩個對象相等,但是這兩個對象竟然同時出現(xiàn)再hashSet中群扶,hashSet中是不會出現(xiàn)兩個相同的元素的及刻。?

那問題在哪里镀裤?

hashset在添加一個元素的時候,會做如下判斷:

1缴饭、如果添加元素的hashcode相等并且 對象equals為true或?qū)ο?= 時暑劝,則認(rèn)為是同一個元素,不添加到新元素中颗搂。

2担猛、如果不符合上述條件,則認(rèn)為是一個新元素丢氢,添加到set中傅联。

所以,雖然p1和p2equals比較時相等疚察,但是hashcode并不一樣蒸走,所以在往set中添加的時候認(rèn)為是兩個不同的元素,所以才會出現(xiàn)了p1和p2同時在set中的情況貌嫡。

我們改進下载碌,復(fù)寫一下hashcode方法,如下:


class Person {

int age;

? ? Stringname;

? ? public Person(int age, String name) {

super();

? ? ? ? this.age = age;

? ? ? ? this.name = name;

? ? }

public int getAge() {

return age;

? ? }

public void setAge(int age) {

this.age = age;

? ? }

public StringgetName() {

return name;

? ? }

public void setName(String name) {

this.name = name;

? ? }

@Overridepublicint hashCode() {

final int prime =31;

? ? ? ? int result =1;

? ? ? ? result = prime * result +age;

? ? ? ? result = prime * result + ((name ==null) ?0 :name.hashCode());

? ? ? ? return result;

? ? }

@Overridepublicboolean equals(Object obj) {

if (this == obj)

return true;

? ? ? ? if (obj ==null)

return false;

? ? ? ? if (getClass() != obj.getClass())

return false;

? ? ? ? Person other = (Person) obj;

? ? ? ? if (age != other.age)

return false;

? ? ? ? if (name ==null) {

if (other.name !=null)

return false;

? ? ? ? }else if (!name.equals(other.name))

return false;

return true;

? ? }

@Overridepublic StringtoString() {

return "Person [age=" +age +", name=" +name +"]";

? ? }

}


重新執(zhí)行結(jié)果:

p1.equals(p2)=true, p1.hashcode=776160, p2.hashcode=776160

[Person [age=10, name=張三]]

于是看到set中僅有一個Person值了衅枫。

補充幾點:

1嫁艇、新建一個類,尤其是業(yè)務(wù)相關(guān)的對象類的時候弦撩,最好復(fù)寫equals方法步咪。

2、復(fù)寫equals方法時益楼,同時記著要復(fù)寫hashCode方法猾漫,誰能保證說這個對象一定不會出現(xiàn)在hashMap中呢?如果你用的是eclipse的自動代碼生成感凤,你會發(fā)現(xiàn)eclipse中復(fù)寫equals和hashCode是在一起的悯周。

引申出幾個經(jīng)常在面試中問到的問題:

? ? ?1、兩個對象陪竿,如果a.equals(b)==true禽翼,那么a和b是否相等?

? ? ? ? ? 相等族跛,但地址不一定相等闰挡。

? ? ?2、兩個對象礁哄,如果hashcode一樣长酗,那么兩個對象是否相等?

? ? ? ? ? 不一定相等桐绒,判斷兩個對象是否相等夺脾,需要判斷equals是否為true之拨。

原文地址:https://www.dutycode.com/post-140.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咧叭,隨后出現(xiàn)的幾起案子敦锌,更是在濱河造成了極大的恐慌,老刑警劉巖佳簸,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乙墙,死亡現(xiàn)場離奇詭異,居然都是意外死亡生均,警方通過查閱死者的電腦和手機听想,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來马胧,“玉大人汉买,你說我怎么就攤上這事∨寮梗” “怎么了蛙粘?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長威彰。 經(jīng)常有香客問我出牧,道長,這世上最難降的妖魔是什么歇盼? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任舔痕,我火速辦了婚禮,結(jié)果婚禮上豹缀,老公的妹妹穿的比我還像新娘伯复。我一直安慰自己,他們只是感情好邢笙,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布啸如。 她就那樣靜靜地躺著,像睡著了一般氮惯。 火紅的嫁衣襯著肌膚如雪叮雳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天筐骇,我揣著相機與錄音债鸡,去河邊找鬼江滨。 笑死铛纬,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的唬滑。 我是一名探鬼主播告唆,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼棺弊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了擒悬?” 一聲冷哼從身側(cè)響起模她,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎懂牧,沒想到半個月后侈净,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡僧凤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年畜侦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躯保。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡旋膳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出途事,到底是詐尸還是另有隱情验懊,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布尸变,位于F島的核電站义图,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏召烂。R本人自食惡果不足惜歌溉,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骑晶。 院中可真熱鬧痛垛,春花似錦、人聲如沸桶蛔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仔雷。三九已至蹂析,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碟婆,已是汗流浹背电抚。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留竖共,地道東北人蝙叛。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像公给,于是被迫代替她去往敵國和親借帘。 傳聞我的和親對象是個殘疾皇子蜘渣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359