<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java中的hashCode() 和 equals()的若干問(wèn)題解答

          共 14512字,需瀏覽 30分鐘

           ·

          2020-10-12 01:37


          一、hashCode()的作用


          哈希表這個(gè)數(shù)據(jù)結(jié)構(gòu)想必大多數(shù)人都不陌生,而且在很多地方都會(huì)利用到hash表來(lái)提高查找效率。在Java的Object類中有一個(gè)方法:


          public?native?int?hashCode();


          根據(jù)這個(gè)方法的聲明可知,該方法返回一個(gè)int類型的數(shù)值,并且是本地方法,因此在Object類中并沒(méi)有給出具體的實(shí)現(xiàn)。為何Object類需要這樣一個(gè)方法?它有什么作用呢?


          不妨舉個(gè)例子:


          假設(shè)內(nèi)存中有0 1 2 3 4 5 6 7 8這8個(gè)位置,如果我有個(gè)字段叫做ID,那么我要把這個(gè)字段存放在以上8個(gè)位置之一,如果不用HashCode而任意存放,那么當(dāng)查找時(shí)就需要到8個(gè)位置中去挨個(gè)查找。使用HashCode則效率會(huì)快很多,把ID的HashCode%8,然后把ID存放在取得余數(shù)的那個(gè)位置,然后每次查找該類的時(shí)候都可以通過(guò)ID的HashCode%8求余數(shù)直接找到存放的位置了。如果ID的HashCode%8算出來(lái)的位置上本身已經(jīng)有數(shù)據(jù)了怎么辦?這就取決于算法的實(shí)現(xiàn)了,比如ThreadLocal中的做法就是從算出來(lái)的位置向后查找第一個(gè)為空的位置,放置數(shù)據(jù);HashMap的做法就是通過(guò)鏈?zhǔn)浇Y(jié)構(gòu)連起來(lái)。反正,只要保證放的時(shí)候和取的時(shí)候的算法一致就行了。如果ID的HashCode%8相等怎么辦(這種對(duì)應(yīng)的是上句說(shuō)的鏈?zhǔn)浇Y(jié)構(gòu)的場(chǎng)景)?這時(shí)候就需要定義equals了。先通過(guò)HashCode%8來(lái)判斷類在哪一個(gè)位置,再通過(guò)equals來(lái)在這個(gè)位置上尋找需要的類。對(duì)比兩個(gè)類的時(shí)候也差不多,先通過(guò)HashCode比較,假如HashCode相等再判斷equals。如果兩個(gè)類的HashCode都不相同,那么這兩個(gè)類必定是不同的

          ?????

          再舉個(gè)實(shí)際的例子Set。我們知道Set里面的元素是不可以重復(fù)的,那么如何做到?Set是根據(jù)equals()方法來(lái)判斷兩個(gè)元素是否相等的。比方說(shuō)Set里面已經(jīng)有1000個(gè)元素了,那么第1001個(gè)元素進(jìn)來(lái)的時(shí)候,最多可能調(diào)用1000次equals方法,如果equals方法寫得復(fù)雜,對(duì)比的東西特別多,那么效率會(huì)大大降低。使用HashCode就不一樣了,比方說(shuō)HashSet,底層是基于HashMap實(shí)現(xiàn)的,先通過(guò)HashCode取一個(gè)模,這樣一下子就固定到某個(gè)位置了,如果這個(gè)位置上沒(méi)有元素,那么就可以肯定HashSet中必定沒(méi)有和新添加的元素equals的元素,就可以直接存放了,都不需要比較;如果這個(gè)位置上有元素了,逐一比較,比較的時(shí)候先比較HashCode,HashCode都不同接下去都不用比了,肯定不一樣,HashCode相等,再equals比較,沒(méi)有相同的元素就存,有相同的元素就不存。如果原來(lái)的Set里面有相同的元素,只要HashCode的生成方式定義得好(不重復(fù)),不管Set里面原來(lái)有多少元素,只需要執(zhí)行一次的equals就可以了。這樣一來(lái),實(shí)際調(diào)用equals方法的次數(shù)大大降低,提高了效率。

          ?????

          所以hashCode在上面扮演的角色為尋域(尋找某個(gè)對(duì)象在集合中區(qū)域位置)。hashCode可以將集合分成若干個(gè)區(qū)域,每個(gè)對(duì)象都可以計(jì)算出他們的hash碼,可以將hash碼分組,每個(gè)分組對(duì)應(yīng)著某個(gè)存儲(chǔ)區(qū)域,根據(jù)一個(gè)對(duì)象的hash碼就可以確定該對(duì)象所存儲(chǔ)區(qū)域,這樣就大大減少查詢匹配元素的數(shù)量,提高了查詢效率。

          ?????

          即hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實(shí)際上是返回一個(gè)int整數(shù)。這個(gè)哈希碼的作用是確定該對(duì)象在哈希表中的索引位置。


          二、hashCode對(duì)于一個(gè)對(duì)象的重要性

          ?????

          hashCode() 定義在JDK的Object.java中,這就意味著Java中的任何類都包含有hashCode() 函數(shù)。
          ??????

          雖然,每個(gè)Java類都包含hashCode() 函數(shù)。但是,僅僅當(dāng)創(chuàng)建某個(gè)“類的散列表”(關(guān)于“散列表”見下面說(shuō)明)時(shí),該類的hashCode() 才有用(作用是:確定該類的每一個(gè)對(duì)象在散列表中的位置;其它情況下(例如,創(chuàng)建類的單個(gè)對(duì)象,或者創(chuàng)建類的對(duì)象數(shù)組等等),類的hashCode() 沒(méi)有作用。
          ??????

          上面的散列表,指的是:Java集合中本質(zhì)是散列表的類,如HashMap,Hashtable,HashSet。

          ??????

          也就是說(shuō):hashCode() 在散列表中才有用,在其它情況下沒(méi)用。在散列表中hashCode() 的作用是獲取對(duì)象的散列碼,進(jìn)而確定該對(duì)象在散列表中的位置。

          ??????

          OK!至此,我們搞清楚了:hashCode()的作用是獲取散列碼。但是,散列碼是用來(lái)干什么的呢?這里簡(jiǎn)單的介紹一下散列碼的作用。

          ?????

          我們都知道,散列表存儲(chǔ)的是鍵值對(duì)(key-value),它的特點(diǎn)是:能根據(jù)“鍵”快速的檢索出對(duì)應(yīng)的“值”。這其中就利用到了散列碼!散列表的本質(zhì)是通過(guò)數(shù)組實(shí)現(xiàn)的。當(dāng)我們要獲取散列表中的某個(gè)“值”時(shí),實(shí)際上是要獲取數(shù)組中的某個(gè)位置的元素。而數(shù)組的位置,就是通過(guò)“鍵”來(lái)獲取的;更進(jìn)一步說(shuō),數(shù)組的位置,是通過(guò)“鍵”對(duì)應(yīng)的散列碼計(jì)算得到的。

          ?????

          下面我以HashTable為例闡述hashCode對(duì)于一個(gè)對(duì)象的重要性。

          ??????

          一個(gè)對(duì)象勢(shì)必會(huì)存在若干個(gè)屬性,如何選擇屬性來(lái)進(jìn)行散列考驗(yàn)著一個(gè)人的設(shè)計(jì)能力。如果我們將所有屬性進(jìn)行散列,這必定會(huì)是一個(gè)糟糕的設(shè)計(jì),因?yàn)閷?duì)象的hashCode方法無(wú)時(shí)無(wú)刻不是在被調(diào)用,如果太多的屬性參與散列,那么需要的操作數(shù)時(shí)間將會(huì)大大增加,這將嚴(yán)重影響程序的性能。但是如果較少屬相參與散列,散列的多樣性會(huì)削弱,會(huì)產(chǎn)生大量的散列“沖突”,除了不能夠很好的利用空間外,在某種程度也會(huì)影響對(duì)象的查詢效率。其實(shí)這兩者是一個(gè)矛盾體,散列的多樣性會(huì)帶來(lái)性能的降低。

          ??????

          那么如何對(duì)對(duì)象的hashCode進(jìn)行設(shè)計(jì),LZ也沒(méi)有經(jīng)驗(yàn)。從網(wǎng)上查到了這樣一種解決方案:設(shè)置一個(gè)緩存標(biāo)識(shí)來(lái)緩存當(dāng)前的散列碼,只有當(dāng)參與散列的對(duì)象改變時(shí)才會(huì)重新計(jì)算,否則調(diào)用緩存的hashCode,這樣就可以從很大程度上提高性能。

          ??????

          在HashTable計(jì)算某個(gè)對(duì)象在table[]數(shù)組中的索引位置,其代碼如下:


          int?index = (hash & 0x7FFFFFFF) % tab.length;


          為什么要&0x7FFFFFFF?因?yàn)槟承?duì)象的hashCode可能會(huì)為負(fù)值,與0x7FFFFFFF進(jìn)行與運(yùn)算可以確保index為一個(gè)正數(shù)。通過(guò)這步我可以直接定位某個(gè)對(duì)象的位置,所以從理論上來(lái)說(shuō)我們是完全可以利用hashCode直接定位對(duì)象的散列表中的位置,但是為什么會(huì)存在一個(gè)key-value的鍵值對(duì),利用key的hashCode來(lái)存入數(shù)據(jù)而不是直接存放value呢?這就關(guān)系HashTable性能問(wèn)題的最重要的問(wèn)題:Hash沖突!

          ??????

          我們知道沖突的產(chǎn)生是由于不同的對(duì)象產(chǎn)生了相同的散列碼,假如我們?cè)O(shè)計(jì)對(duì)象的散列碼可以確保99.999999999%的不重復(fù),但是有一種絕對(duì)且?guī)缀醪豢赡苡龅降臎_突你是絕對(duì)避免不了的。我們知道hashcode返回的是int,它的值只可能在int范圍內(nèi)。如果我們存放的數(shù)據(jù)超過(guò)了int的范圍呢?這樣就必定會(huì)產(chǎn)生兩個(gè)相同的index,這時(shí)在index位置處會(huì)存儲(chǔ)兩個(gè)對(duì)象,我們就可以利用key本身來(lái)進(jìn)行判斷。所以具有相索引的對(duì)象,在該index位置處存在多個(gè)對(duì)象,我們必須依靠key的hashCode和key本身來(lái)進(jìn)行區(qū)分。


          三、equals()的作用


          equals() 的作用是 用來(lái)判斷兩個(gè)對(duì)象是否相等


          equals() 定義在JDK的Object.java中。通過(guò)判斷兩個(gè)對(duì)象的地址是否相等(即,是否是同一個(gè)對(duì)象)來(lái)區(qū)分它們是否相等。源碼如下:


          public?boolean?equals(Object?obj) {
          ????return?(this?== obj);
          }


          既然Object.java中定義了equals()方法,這就意味著所有的Java類都實(shí)現(xiàn)了equals()方法,所有的類都可以通過(guò)equals()去比較兩個(gè)對(duì)象是否相等。但是,我們已經(jīng)說(shuō)過(guò),使用默認(rèn)的“equals()”方法,等價(jià)于“==”方法。因此,我們通常會(huì)重寫equals()方法:若兩個(gè)對(duì)象的內(nèi)容相等,則equals()方法返回true;否則,返回fasle。


          下面根據(jù)“類是否覆蓋equals()方法”,將它分為2類。


          (01) 若某個(gè)類沒(méi)有覆蓋equals()方法,當(dāng)它的通過(guò)equals()比較兩個(gè)對(duì)象時(shí),實(shí)際上是比較兩個(gè)對(duì)象是不是同一個(gè)對(duì)象。這時(shí),等價(jià)于通過(guò)“==”去比較這兩個(gè)對(duì)象。
          (02) 我們可以覆蓋類的equals()方法,來(lái)讓equals()通過(guò)其它方式比較兩個(gè)對(duì)象是否相等。通常的做法是:若兩個(gè)對(duì)象的內(nèi)容相等,則equals()方法返回true;否則,返回fasle。


          下面,舉例對(duì)上面的2種情況進(jìn)行說(shuō)明。


          1、"沒(méi)有覆蓋equals()方法"的情況


          代碼如下:


          package com.demo;

          public?class?EqualsTest1?{
          ????
          ????/**
          ?????* Person類
          ?????* @author lixiaoxi
          ?????*
          ?????*/

          ????private?static?class?Person{
          ????????int?age;
          ????????String name;
          ????????
          ????????public?Person(String name, int?age) {
          ????????????this.name = name;
          ????????????this.age = age;
          ????????}

          ????????public?String toString() {
          ????????????return?name + " - "?+age;
          ????????}
          ????}
          ????
          ????public?static?void?main(String[] args) {
          ????????// 新建2個(gè)相同內(nèi)容的Person對(duì)象,
          ????????// 再用equals比較它們是否相等
          ????????Person p1 = new?Person("eee", 100);
          ????????Person p2 = new?Person("eee", 100);
          ????????System.out.printf("%s\n", p1.equals(p2));
          ????}
          ????
          }


          運(yùn)行結(jié)果:false


          結(jié)果分析:
          ?????

          我們通過(guò) p1.equals(p2) 來(lái)“比較p1和p2是否相等時(shí)”。實(shí)際上,調(diào)用的Object.java的equals()方法,即調(diào)用的 (p1==p2) 。它是比較“p1和p2是否是同一個(gè)對(duì)象”。而由 p1 和 p2 的定義可知,它們雖然內(nèi)容相同;但它們是兩個(gè)不同的對(duì)象!因此,返回結(jié)果是false。


          2、"覆蓋equals()方法"的情況


          我們修改上面的EqualsTest1.java:覆蓋equals()方法。


          代碼如下:


          package com.demo;

          public?class?EqualsTest2?{
          ????
          ????/**
          ?????* Person類
          ?????* @author lixiaoxi
          ?????*
          ?????*/

          ????private?static?class?Person{
          ????????int?age;
          ????????String name;
          ????????
          ????????public?Person(String name, int?age) {
          ????????????this.name = name;
          ????????????this.age = age;
          ????????}

          ????????public?String toString() {
          ????????????return?name + " - "?+age;
          ????????}
          ????????
          ????????/**
          ?????????* 覆蓋equals方法
          ?????????*/

          ????????@Override
          ????????public?boolean equals(Object obj)
          {
          ????????????if(obj == null){
          ????????????????return?false;
          ????????????}
          ????????????
          ????????????//如果是同一個(gè)對(duì)象返回true,反之返回false
          ????????????if(this?== obj){
          ????????????????return?true;
          ????????????}
          ????????????
          ????????????//判斷是否類型相同
          ????????????if(this.getClass() != obj.getClass()){
          ????????????????return?false;
          ????????????}
          ????????????
          ????????????Person person = (Person)obj;
          ????????????return?name.equals(person.name) && age==person.age;
          ????????}
          ????}
          ????
          ????public?static?void?main(String[] args) {
          ????????// 新建2個(gè)相同內(nèi)容的Person對(duì)象,
          ????????// 再用equals比較它們是否相等
          ????????Person p1 = new?Person("eee", 100);
          ????????Person p2 = new?Person("eee", 100);
          ????????System.out.printf("%s\n", p1.equals(p2));
          ????}
          }


          運(yùn)行結(jié)果:true


          結(jié)果分析:


          我們?cè)贓qualsTest2.java 中重寫了Person的equals()函數(shù):當(dāng)兩個(gè)Person對(duì)象的 name 和 age 都相等,則返回true。因此,運(yùn)行結(jié)果返回true。


          講到這里,順便說(shuō)一下java對(duì)equals()的要求。有以下幾點(diǎn):


          1. 對(duì)稱性:如果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不同類型的對(duì)象)永遠(yuǎn)返回是"false"。


          四、equals()與==的區(qū)別


          == : 它的作用是判斷兩個(gè)對(duì)象的地址是不是相等。即,判斷兩個(gè)對(duì)象是不是同一個(gè)對(duì)象。


          equals() : 它的作用也是判斷兩個(gè)對(duì)象是否相等。但它一般有兩種使用情況(上面已詳細(xì)介紹過(guò)):


          情況1,類沒(méi)有覆蓋equals()方法。則通過(guò)equals()比較該類的兩個(gè)對(duì)象時(shí),等價(jià)于通過(guò)“==”比較這兩個(gè)對(duì)象。


          情況2,類覆蓋了equals()方法。一般,我們都覆蓋equals()方法來(lái)比較兩個(gè)對(duì)象的內(nèi)容相等;若它們的內(nèi)容相等,則返回true(即,認(rèn)為這兩個(gè)對(duì)象相等)。


          下面,通過(guò)示例比較它們的區(qū)別。


          代碼如下:


          package com.demo;

          public?class?EqualsTest3?{
          ????
          ????/**
          ?????* Person類
          ?????* @author lixiaoxi
          ?????*
          ?????*/

          ????private?static?class?Person{
          ????????int?age;
          ????????String name;
          ????????
          ????????public?Person(String name, int?age) {
          ????????????this.name = name;
          ????????????this.age = age;
          ????????}

          ????????public?String toString() {
          ????????????return?name + " - "?+age;
          ????????}
          ????????
          ????????/**
          ?????????* 覆蓋equals方法
          ?????????*/

          ????????@Override
          ????????public?boolean equals(Object obj)
          {
          ????????????if(obj == null){
          ????????????????return?false;
          ????????????}
          ????????????
          ????????????//如果是同一個(gè)對(duì)象返回true,反之返回false
          ????????????if(this?== obj){
          ????????????????return?true;
          ????????????}
          ????????????
          ????????????//判斷是否類型相同
          ????????????if(this.getClass() != obj.getClass()){
          ????????????????return?false;
          ????????????}
          ????????????
          ????????????Person person = (Person)obj;
          ????????????return?name.equals(person.name) && age==person.age;
          ????????}
          ????}
          ????
          ????public?static?void?main(String[] args) {
          ????????// 新建2個(gè)相同內(nèi)容的Person對(duì)象,
          ????????// 再用equals比較它們是否相等
          ????????Person p1 = new?Person("eee", 100);
          ????????Person p2 = new?Person("eee", 100);
          ????????System.out.printf("p1.equals(p2) : %s\n", p1.equals(p2));
          ????????System.out.printf("p1==p2 : %s\n", p1==p2);
          ????}
          }


          運(yùn)行結(jié)果:


          p1.equals(p2) : true
          p1==p2 : false


          結(jié)果分析:


          在EqualsTest3.java 中:


          (01) p1.equals(p2) 這是判斷p1和p2的內(nèi)容是否相等。因?yàn)镻erson覆蓋equals()方法,而這個(gè)equals()是用來(lái)判斷p1和p2的內(nèi)容是否相等,恰恰p1和p2的內(nèi)容又相等;因此,返回true。


          (02) p1==p2 這是判斷p1和p2是否是同一個(gè)對(duì)象。由于它們是各自新建的兩個(gè)Person對(duì)象;因此,返回false。


          五、hashCode() 和 equals()的關(guān)系


          我們以“類的用途”來(lái)將“hashCode() 和 equals()的關(guān)系”分2種情況來(lái)說(shuō)明。


          1、第一種 不會(huì)創(chuàng)建“類對(duì)應(yīng)的散列表”


          這里所說(shuō)的“不會(huì)創(chuàng)建類對(duì)應(yīng)的散列表”是說(shuō):我們不會(huì)在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,不會(huì)創(chuàng)建該類的HashSet集合。


          在這種情況下,該類的“hashCode() 和 equals() ”沒(méi)有半毛錢關(guān)系的!這種情況下,equals() 用來(lái)比較該類的兩個(gè)對(duì)象是否相等。而hashCode() 則根本沒(méi)有任何作用,所以,不用理會(huì)hashCode()。


          下面,我們通過(guò)示例查看類的兩個(gè)對(duì)象相等 以及 不等時(shí)hashCode()的取值。


          代碼如下:


          package com.demo;

          /**
          ?* @desc 比較equals() 返回true 以及 返回false時(shí), hashCode()的值。
          ?* @author lixiaoxi
          ?*
          ?*/

          public?class?NormalHashCodeTest?{
          ????
          ??????/**
          ?????* @desc Person類。
          ?????*/

          ????private?static?class?Person?{
          ????????int?age;
          ????????String name;

          ????????public?Person(String name, int?age) {
          ????????????this.name = name;
          ????????????this.age = age;
          ????????}

          ????????public?String toString() {
          ????????????return?name + " - "?+age;
          ????????}

          ????????/**
          ?????????* @desc 覆蓋equals方法
          ?????????*/
          ??
          ????????public?boolean equals(Object obj){
          ????????????if(obj == null){
          ????????????????return?false;
          ????????????}
          ??????????????
          ????????????//如果是同一個(gè)對(duì)象返回true,反之返回false
          ????????????if(this?== obj){
          ????????????????return?true;
          ????????????}
          ??????????????
          ????????????//判斷是否類型相同
          ????????????if(this.getClass() != obj.getClass()){
          ????????????????return?false;
          ????????????}
          ??????????????
          ????????????Person person = (Person)obj;
          ????????????return?name.equals(person.name) && age==person.age;
          ????????}
          ????}
          ????
          ????public?static?void?main(String[] args) {
          ????????// 新建2個(gè)相同內(nèi)容的Person對(duì)象,
          ????????// 再用equals比較它們是否相等
          ????????Person p1 = new?Person("eee", 100);
          ????????Person p2 = new?Person("eee", 100);
          ????????Person p3 = new?Person("aaa", 200);
          ????????System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
          ????????System.out.printf("p1.equals(p3) : %s; p1(%d) p3(%d)\n", p1.equals(p3), p1.hashCode(), p3.hashCode());
          ????}
          }


          運(yùn)行結(jié)果:


          p1.equals(p2) : true; p1(30961619) p2(521452)
          p1.equals(p3) : false; p1(30961619) p3(29744585)


          從結(jié)果也可以看出:p1和p2相等的情況下,hashCode()也不一定相等。


          2、第二種 會(huì)創(chuàng)建“類對(duì)應(yīng)的散列表”


          這里所說(shuō)的“會(huì)創(chuàng)建類對(duì)應(yīng)的散列表”是說(shuō):我們會(huì)在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,會(huì)創(chuàng)建該類的HashSet集合。

          在這種情況下,該類的“hashCode() 和 equals() ”是有關(guān)系的:


          1)、如果兩個(gè)對(duì)象相等,那么它們的hashCode()值一定相同。這里的相等是指,通過(guò)equals()比較兩個(gè)對(duì)象時(shí)返回true。


          2)、如果兩個(gè)對(duì)象hashCode()相等,它們并不一定相等。


          因?yàn)樵谏⒘斜碇校琱ashCode()相等,即兩個(gè)鍵值對(duì)的哈希值相等。然而哈希值相等,并不一定能得出鍵值對(duì)相等。補(bǔ)充說(shuō)一句:“兩個(gè)不同的鍵值對(duì),哈希值相等”,這就是哈希沖突。


          此外,在這種情況下。若要判斷兩個(gè)對(duì)象是否相等,除了要覆蓋equals()之外,也要覆蓋hashCode()函數(shù)。否則,equals()無(wú)效。例如,創(chuàng)建Person類的HashSet集合,必須同時(shí)覆蓋Person類的equals() 和 hashCode()方法。如果單單只是覆蓋equals()方法。我們會(huì)發(fā)現(xiàn),equals()方法沒(méi)有達(dá)到我們想要的效果。


          代碼如下:


          package com.demo;

          import java.util.HashSet;

          /**
          ?* @desc 比較equals() 返回true 以及 返回false時(shí), hashCode()的值。
          ?* @author lixiaoxi
          ?*
          ?*/

          public?class?ConflictHashCodeTest1?{
          ????
          ?????/**
          ?????* @desc Person類。
          ?????*/

          ????private?static?class?Person?{
          ????????int?age;
          ????????String name;

          ????????public?Person(String name, int?age) {
          ????????????this.name = name;
          ????????????this.age = age;
          ????????}

          ????????public?String toString() {
          ????????????return?"("+name + ", "?+age+")";
          ????????}

          ????????/**
          ?????????* @desc 覆蓋equals方法
          ?????????*/
          ??
          ????????@Override
          ????????public?boolean equals(Object obj)
          {
          ????????????if(obj == null){
          ????????????????return?false;
          ????????????}
          ??????????????
          ????????????//如果是同一個(gè)對(duì)象返回true,反之返回false
          ????????????if(this?== obj){
          ????????????????return?true;
          ????????????}
          ??????????????
          ????????????//判斷是否類型相同
          ????????????if(this.getClass() != obj.getClass()){
          ????????????????return?false;
          ????????????}
          ??????????????
          ????????????Person person = (Person)obj;
          ????????????return?name.equals(person.name) && age==person.age;
          ????????}
          ????}
          ????
          ????public?static?void?main(String[] args) {
          ????????// 新建Person對(duì)象,
          ????????Person p1 = new?Person("eee", 100);
          ????????Person p2 = new?Person("eee", 100);
          ????????Person p3 = new?Person("aaa", 200);

          ????????// 新建HashSet對(duì)象
          ????????HashSet set?= new?HashSet();
          ????????set.add(p1);
          ????????set.add(p2);
          ????????set.add(p3);

          ????????// 比較p1 和 p2, 并打印它們的hashCode()
          ????????System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
          ????????// 打印set
          ????????System.out.printf("set:%s\n", set);
          ????}
          ????
          }


          運(yùn)行結(jié)果:


          p1.equals(p2) : true; p1(13905160) p2(30961619)
          set:[(aaa, 200), (eee, 100), (eee, 100)]


          結(jié)果分析:


          我們重寫了Person的equals()。但是,很奇怪的發(fā)現(xiàn):HashSet中仍然有重復(fù)元素:p1 和 p2。為什么會(huì)出現(xiàn)這種情況呢?這是因?yàn)殡m然p1 和 p2的內(nèi)容相等,但是它們的hashCode()不等;所以,HashSet在添加p1和p2的時(shí)候,認(rèn)為它們不相等。


          下面,我們同時(shí)覆蓋equals() 和 hashCode()方法。


          代碼如下:


          package com.demo;

          import java.util.HashSet;

          /**
          ?* @desc 比較equals() 返回true 以及 返回false時(shí), hashCode()的值。
          ?* @author lixiaoxi
          ?*
          ?*/

          public?class?ConflictHashCodeTest2?{
          ????
          ????/**
          ?????* @desc Person類。
          ?????*/

          ????private?static?class?Person?{
          ????????int?age;
          ????????String name;

          ????????public?Person(String name, int?age) {
          ????????????this.name = name;
          ????????????this.age = age;
          ????????}

          ????????public?String toString() {
          ????????????return?name + " - "?+age;
          ????????}

          ????????/**
          ?????????* @desc重寫hashCode
          ?????????*/
          ??
          ????????@Override
          ????????public?int?hashCode()
          {
          ????????????int?nameHash = name.toUpperCase().hashCode();
          ????????????return?nameHash ^ age;
          ????????}

          ????????/**
          ?????????* @desc 覆蓋equals方法
          ?????????*/
          ??
          ????????@Override
          ????????public?boolean equals(Object obj)
          {
          ????????????if(obj == null){
          ????????????????return?false;
          ????????????}
          ??????????????
          ????????????//如果是同一個(gè)對(duì)象返回true,反之返回false
          ????????????if(this?== obj){
          ????????????????return?true;
          ????????????}
          ??????????????
          ????????????//判斷是否類型相同
          ????????????if(this.getClass() != obj.getClass()){
          ????????????????return?false;
          ????????????}
          ??????????????
          ????????????Person person = (Person)obj;
          ????????????return?name.equals(person.name) && age==person.age;
          ????????}
          ????}
          ????
          ????public?static?void?main(String[] args) {
          ????????// 新建Person對(duì)象,
          ????????Person p1 = new?Person("eee", 100);
          ????????Person p2 = new?Person("eee", 100);
          ????????Person p3 = new?Person("aaa", 200);
          ????????Person p4 = new?Person("EEE", 100);

          ????????// 新建HashSet對(duì)象
          ????????HashSet set?= new?HashSet();
          ????????set.add(p1);
          ????????set.add(p2);
          ????????set.add(p3);

          ????????// 比較p1 和 p2, 并打印它們的hashCode()
          ????????System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
          ????????// 比較p1 和 p4, 并打印它們的hashCode()
          ????????System.out.printf("p1.equals(p4) : %s; p1(%d) p4(%d)\n", p1.equals(p4), p1.hashCode(), p4.hashCode());
          ????????// 打印set
          ????????System.out.printf("set:%s\n", set);
          ????}
          }


          運(yùn)行結(jié)果:


          p1.equals(p2) : true; p1(68545) p2(68545)
          p1.equals(p4) : false; p1(68545) p4(68545)
          set:[aaa - 200, eee - 100]


          結(jié)果分析:


          這下,equals()生效了,HashSet中沒(méi)有重復(fù)元素。


          比較p1和p2,我們發(fā)現(xiàn):它們的hashCode()相等,通過(guò)equals()比較它們也返回true。所以,p1和p2被視為相等。


          比較p1和p4,我們發(fā)現(xiàn):雖然它們的hashCode()相等;但是,通過(guò)equals()比較它們返回false。所以,p1和p4被視為不相等。


          原文鏈接:cnblogs.com/xiaoxi/p/6428432.html



          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩色区| 欧美性猛交XXXX乱大交HD | 乱伦小说五月天 | 大香蕉网免费伊人 | 五月婷婷激情在线 |