天天看點

Java 基礎:hashCode方法一、前言二、hashCode方法三、兩個小例子四、結論和忠告

    泥瓦匠最近被項目搞的天昏地暗。發現有些要給自己一些目标,關于技術的目标:

專注很重要。專注java 基礎 + h5(學習)

    其他作業系統,算法,資料結構當成課外書博覽。有時候,就是那樣你越是專注方面越多對自己打擊越大學啥啥都不好。今天帶來java基礎:hashcode方法

    hash code(散列碼,也可以叫哈希碼值)是對象産生的一個整型值。其生成沒有規律的。二者散列碼可以擷取對象中的資訊,轉成那個對象的“相對唯一”的整型值。所有對象都有一個散列碼,hashcode()是根類 object 的一個方法。散清單的工作原理在java基礎不展開講,隻要知道它是一種快速的“字典”即可。下面引用老外一張圖:

Java 基礎:hashCode方法一、前言二、hashCode方法三、兩個小例子四、結論和忠告

    首先泥瓦匠引用一段來自 object規範 【javase6】:

hashcode的正常協定是: 1、在 java 應用程式執行期間,在對同一對象多次調用 hashcode 方法時,必須一緻地傳回相同的整數,前提是将對象進行 equals 比較時所用的資訊沒有被修改。從某一應用程式的一次執行到同一應用程式的另一次執行,該整數無需保持一緻。 2、如果根據 equals(object) 方法,兩個對象是相等的,那麼對這兩個對象中的每個對象調用 <code>hashcode</code> 方法都必須生成相同的整數結果。 3、如果根據equals方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用 hashcode 方法不 要求一定生成不同的整數結果。但是,程式員應該意識到,為不相等的對象生成不同整數結果可以提高哈希表的性能。

    由于hashcode定義在根類object,是以每個對象都是object,都具有一個預設的散列值,即是對象的存儲位址。泥瓦匠請大家看一下這個例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<code>public class hashcodetest</code>

<code>{</code>

<code>    </code><code>public static void main(string[] args)</code>

<code>    </code><code>{</code>

<code>        </code><code>string s = "hashcode";</code>

<code>        </code><code>stringbuilder sb = new stringbuilder(s);</code>

<code>        </code><code>system.out.println("hashcode1: " + s.hashcode() + " " + sb.hashcode());</code>

<code>        </code> 

<code>        </code><code>string s1 = new string("hashcode");</code>

<code>        </code><code>stringbuilder sb1 = new stringbuilder(s1);</code>

<code>        </code><code>system.out.println("hashcode2: " + s1.hashcode() + " " + sb1.hashcode());</code>

<code>        </code><code>// are they equals?</code>

<code>        </code><code>system.out.println("s  s1 : " + s.equals(s1));</code>

<code>        </code><code>system.out.println("sb sb1: " + sb.equals(sb1));</code>

<code>    </code><code>}</code>

<code>}</code>

   run 一下,可以在控制台看到:

<code>hashcode1: 147696667 1385112968</code>

<code>hashcode2: 147696667 870919696</code>

<code>s  s1 : true</code>

<code>sb sb1: false</code>

   泥瓦匠小結:

1、s 與 s1相等,且hashcode一樣。驗證了【hashcode的正常協定】的第二條。原因是字元串的散列碼由内容導出的。(這個第二個例子我們會驗證)

2、stringbuilder

裡面沒有定義hashcode方法,是以導出的是object預設的對對象存儲的位址。(注意到object的hashcode方法前面有個native

    泥瓦匠剛剛提到字元串散列碼是由内容導出的。下面看看string的hashcode的實作。

18

19

20

21

22

23

24

25

<code>   </code><code>/** the value is used for character storage */</code>

<code>private char value[];</code>

<code>private int hash;// default to 0</code>

<code>/**</code>

<code> </code><code>* returns a hash code for this string. the hash code for a</code>

<code> </code><code>* string object is computed as</code>

<code> </code><code>* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]</code>

<code> </code><code>*/</code>

<code>public int hashcode()</code>

<code>    </code><code>int h = hash;</code>

<code>    </code><code>if (h == 0 &amp;&amp; value.length &gt; 0)</code>

<code>        </code><code>char val[] = value;</code>

<code>        </code><code>for (int i = 0; i &lt; value.length; i++)</code>

<code>        </code><code>{</code>

<code>            </code><code>h = 31 * h + val[i];</code>

<code>        </code><code>}</code>

<code>        </code><code>hash = h;</code>

<code>    </code><code>return h;</code>

    泥瓦匠小結:

1、s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]  數學公式代表什麼?

s[i]是string的第i個字元,n是string的長度。31為啥呢?下面引用《effective java》的原話:

之是以選擇31,是因為它是個奇素數,如果乘數是偶數,并且乘法溢出的話,資訊就會丢失,因為與2相乘等價于移位運算。使用素數的好處并不是很明 顯,但是習慣上都使用素數來計算散列結果。31有個很好的特性,就是用移位和減法來代替乘法,可以得到更好的性能:31*i==(i&lt;&lt; 5)-i。現在的vm可以自動完成這種優化。

确實hashcode有點晦澀,有可能是因為那個數學散列函數。下面是《effective java》中的結論點:

1、如果對象有相同的散列碼,被映射到同一個散列桶,這樣散清單退化稱為 連結清單 ,這樣性能降低。 2、相等的對象必須具有相等的散列碼 3、為不相等的對象産生不相等的散列碼 4、不要試圖從散列碼計算中排除掉一個對象關鍵部分來提高性能