【Java概念每日一題004】兩個對象hashCode()相同,則equals()也一定為true?


原文鏈接:https://www.cnblogs.com/Qian123/p/5703507.html#_label1

hashcode() 方法詳解

hashCode()方法給對象返回一個hash code值。這個方法被用於hash tables,例如HashMap。

它的性質是:

  • 在一個Java應用的執行期間,如果一個對象提供給equals做比較的信息沒有被修改的話,該對象多次調用hashCode()方法,該方法必須始終如一返回同一個integer。

  • 如果兩個對象根據equals(Object)方法是相等的,那么調用二者各自的hashCode()方法必須產生同一個integer結果。

  • 並不要求根據equals(java.lang.Object)方法不相等的兩個對象,調用二者各自的hashCode()方法必須產生不同的integer結果。然而,程序員應該意識到對於不同的對象產生不同的integer結果,有可能會提高hash table的性能。

大量的實踐表明,由Object類定義的hashCode()方法對於不同的對象返回不同的integer。

在object類中,hashCode定義如下:

public native int hashCode();

說明是一個本地方法,它的實現是根據本地機器相關的。當然我們可以在自己寫的類中覆蓋hashcode()方法,比如String、Integer、Double等這些類都是覆蓋了hashcode()方法的。例如在String類中定義的hashcode()方法如下:

public int hashCode() {  
    int h = hash;  
    if (h == 0) {  
        int off = offset;  
        char val[] = value;  
        int len = count;  
  
        for (int i = 0; i < len; i++) {  
            h = 31 * h + val[off++];  
        }  
        hash = h;  
    }  
    return h;  
}

(String的API中寫到):s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

使用 int 算法,這里 s[i] 是字符串的第 i 個字符,n 是字符串的長度,^ 表示求冪(空字符串的哈希碼為 0)。

想要弄明白hashCode的作用,必須要先知道Java中的集合。  
       總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。前者集合內的元素是有序的,元素可以重復;后者元素無序,但元素不可重復。這里就引出一個問題:要想保證元素不重復,可兩個元素是否重復應該依據什么來判斷呢?
        這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那么當元素很多時,后添加到集合中的元素比較的次數就非常多了。也就是說,如果集合中現在已經有1000個元素,那么第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。   
       於是,Java采用了哈希表的原理。哈希(Hash)實際上是個人名,由於他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也稱為散列算法,是將數據依特定算法直接指定到一個地址上,初學者可以簡單理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並不是)。  
       這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。所以這里存在一個沖突解決的問題。這樣一來實際調用equals方法的次數就大大降低了,幾乎只需要一兩次。  

 

簡而言之,在集合查找時,hashcode能大大降低對象比較次數,提高查找效率!

Java對象的eqauls方法和hashCode方法是這樣規定的:

1、相等(相同)的對象必須具有相等的哈希碼(或者散列碼)。

2、如果兩個對象的hashCode相同,它們並不一定相同。

 

以下是Object對象API關於equal方法和hashCode方法的說明:

  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
  • 以上API說明是對之前2點的官方詳細說明

關於第一點,相等(相同)的對象必須具有相等的哈希碼(或者散列碼),為什么?

想象一下,假如兩個Java對象A和B,A和B相等(eqauls結果為true),但A和B的哈希碼不同,則A和B存入HashMap時的哈希碼計算得到的HashMap內部數組位置索引可能不同,那么A和B很有可能允許同時存入HashMap,顯然相等/相同的元素是不允許同時存入HashMap,HashMap不允許存放重復元素。

 

關於第二點,兩個對象的hashCode相同,它們並不一定相同

也就是說,不同對象的hashCode可能相同;假如兩個Java對象A和B,A和B不相等(eqauls結果為false),但A和B的哈希碼相等,將A和B都存入HashMap時會發生哈希沖突,也就是A和B存放在HashMap內部數組的位置索引相同這時HashMap會在該位置建立一個鏈接表,將A和B串起來放在該位置,顯然,該情況不違反HashMap的使用原則,是允許的。當然,哈希沖突越少越好,盡量采用好的哈希算法以避免哈希沖突。

 

 所以,Java對於eqauls方法和hashCode方法是這樣規定的:

 

1.如果兩個對象相同,那么它們的hashCode值一定要相同;

2.如果兩個對象的hashCode相同,它們並不一定相同(這里說的對象相同指的是用eqauls方法比較)。  

如不按要求去做了,會發現相同的對象可以出現在Set集合中,同時,增加新元素的效率會大大下降。

3.equals()相等的兩個對象,hashcode()一定相等;equals()不相等的兩個對象,卻並不能證明他們的hashcode()不相等。

        換句話說,equals()方法不相等的兩個對象,hashcode()有可能相等(我的理解是由於哈希碼在生成的時候產生沖突造成的)。反過來,hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

 

在object類中,hashcode()方法是本地方法,返回的是對象的地址值,而object類中的equals()方法比較的也是兩個對象的地址值,如果equals()相等,說明兩個對象地址值也相等,當然hashcode()也就相等了;在String類中,equals()返回的是兩個對象內容的比較,當兩個對象內容相等時,Hashcode()方法根據String類的重寫代碼的分析,也可知道hashcode()返回結果也會相等。以此類推,可以知道Integer、Double等封裝類中經過重寫的equals()和hashcode()方法也同樣適合於這個原則。當然沒有經過重寫的類,在繼承了object類的equals()和hashcode()方法后,也會遵守這個原則。

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM