細說Java中hashCode

一、作用

    HashCode的官方文檔定義是這樣寫的:   

 hashcode方法返回該對象的哈希碼值。支持該方法是爲哈希表提供一些優點,例如,java.util.Hashtable 提供的哈希表。 

hashCode 的常規協定是: 
在 Java 應用程序執行期間,在同一對象上多次調用 hashCode 方法時,必須一致地返回相同的整數,前提是對象上 equals 比較中所用的信息沒有被修改。從某一應用程序的一次執行到同一應用程序的另一次執行,該整數無需保持一致。 
如果根據 equals(Object) 方法,兩個對象是相等的,那麼在兩個對象中的每個對象上調用 hashCode 方法都必須生成相同的整數結果。 
以下情況不 是必需的:如果根據 equals(java.lang.Object) 方法,兩個對象不相等,那麼在兩個對象中的任一對象上調用 hashCode 方法必定會生成不同的整數結果。但是,程序員應該知道,爲不相等的對象生成不同整數結果可以提高哈希表的性能。 
實際上,由 Object 類定義的 hashCode 方法確實會針對不同的對象返回不同的整數。(這一般是通過將該對象的內部地址轉換成一個整數來實現的,但是 JavaTM 編程語言不需要這種實現技巧。) 

當equals方法被重寫時,通常有必要重寫 hashCode 方法,以維護 hashCode 方法的常規協定,該協定聲明相等對象必須具有相等的哈希碼
 

總結下:

1、hashCode的存在主要是用於查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結構中確定對象的存儲地址的;

2、如果兩個對象相同,就是適用於equals(java.lang.Object) 方法,那麼這兩個對象的hashCode一定要相同;

3、如果對象的equals方法被重寫,那麼對象的hashCode也儘量重寫,並且產生hashCode使用的對象,一定要和equals方法中使用的一致,否則就會違反上面提到的第2點;

4、兩個對象的hashCode相同,並不一定表示兩個對象就相同,也就是不一定適用於equals(java.lang.Object) 方法,只能夠說明這兩個對象在散列存儲結構中,如Hashtable,他們“存放在同一個籃子裏”。
 

對於查找的快捷性,以下例子方便理解
   例如內存中有這樣的位置
   0  1  2  3  4  5  6  7  
   而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放後,那麼當查找時就需要到這八個位置裏挨個去找,或者用二分法一類的算法。但如果用hashcode那就會使效率提高很多。我們這個類中有個字段叫ID,那麼我們就定義我們的hashcode爲ID%8,然後把我們的類存放在取得得餘數那個位置。比如我們的ID爲9,9除8的餘數爲1,那麼我們就把該類存在1這個位置,如果ID是13,求得的餘數是5,那麼我們就把該類放在5這個位置。這樣,以後在查找該類時就可以通過ID除 8求餘數直接找到存放的位置了。

     但是如果兩個類有相同的hashcode怎麼辦那(我們假設上面的類的ID不是唯一的),例如9除以8和17除以8的餘數都是1,那麼這是不是合法的,回答是:可以這樣。那麼如何判斷呢?在這個時候就需要定義 equals了。
也就是說,我們先通過 hashcode來判斷兩個類是否存放某個桶裏,但這個桶裏可能有很多類,那麼我們就需要再通過 equals 來在這個桶裏找到我們要的類。如果光重寫了equals(),就如只知道有相同的類,卻不知道在那個桶裏,結果還是找不到這個類,所以必須同時重寫兩個才行!
 

二、如何重寫

1、不能包含equals方法中沒有的字段,否則會導致相等的對象可能會有不同的哈希值。

    (即對類中每一個重要字段,也就是影響對象的值的字段,也就是equals方法裏有比較的字段,進行操作)

2、String對象和Bigdecimal對象已經重寫了hashcode方法,這些類型的值可以直接用於重寫hashcode方法;

3、result = 31 *result + (dishCode !=null ?dishCode.hashCode() : 0);,這裏面爲啥用個31來計算,而且很多人都是這麼寫的,這是因爲31是個神奇的數字,任何數n*31都可以被jvm優化爲(n<<5)-n,移位和減法的操作效率比乘法的操作效率高很多!

4、Google首席Java架構師Joshua Bloch在他的著作《Effective Java》中提出了一種簡單通用的hashCode算法:

     ①初始化一個整形變量,爲此變量賦予一個非零的常數值,比如int result = 17;

     ②如果是對象應用(例如有String類型的字段),如果equals方法中採取遞歸調用的比較方式,那麼hashCode中同樣採取遞歸調用hashCode的方式。否則需要爲這個域計算一個範式,比如當這個域的值爲null的時候(即 String a = null 時),那麼hashCode值爲0

    

三、 與內存地址關係

   與內存地址完全不同的

具體詳情請點擊

  java對象的內存地址與hashcode值關係

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章