Java 基礎:hashCode方法

Writer:BYSocket(泥沙磚瓦漿木匠)java

微博:BYSocket程序員

豆瓣:BYSocket
web

1、前言

    泥瓦匠最近被項目搞的天昏地暗。發現有些要給本身一些目標,關於技術的目標:
算法

專一很重要。專一Java 基礎 + H5(學習)數據結構

    其餘操做系統,算法,數據結構當成課外書博覽。有時候,就是那樣你越是專一方面越多對本身打擊越大學啥啥都很差。今天帶來Java基礎:hashCode方法socket

2、hashCode方法

    hash code(散列碼,也能夠叫哈希碼值)是對象產生的一個整型值。其生成沒有規律的。兩者散列碼能夠獲取對象中的信息,轉成那個對象的「相對惟一」的整型值。全部對象都有一個散列碼,hashCode()是根類 Object 的一個方法。散列表的工做原理在Java基礎不展開講,只要知道它是一種快速的「字典」便可。下面引用老外一張圖:函數

3、兩個小例子

    首先泥瓦匠引用一段來自 Object規範 【JavaSE6】:性能

hashCode的常規協定是:學習

            • 一、在 Java 應用程序執行期間,在對同一對象屢次調用 hashCode 方法時,必須一致地返回相同的整數,前提是將對象進行 equals 比較時所用的信息沒有被修改。從某一應用程序的一次執行到同一應用程序的另外一次執行,該整數無需保持一致。優化

  • 二、若是根據 equals(Object) 方法,兩個對象是相等的,那麼對這兩個對象中的每一個對象調用 hashCode 方法都必須生成相同的整數結果。

    • 三、若是根據equals方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用 hashCode 方法 要求必定生成不一樣的整數結果。可是,程序員應該意識到,爲不相等的對象生成不一樣整數結果能夠提升哈希表的性能。

  •     因爲hashCode定義在根類Object,因此每一個對象都是Object,都具備一個默認的散列值,便是對象的存儲地址。泥瓦匠請你們看一下這個例子:

public class HashCodeTest
{
    public static void main(String[] args)
    {
        String s = "hashCode";
        StringBuilder sb = new StringBuilder(s);
        System.out.println("hashCode1: " + s.hashCode() + " " + sb.hashCode());
         
        String s1 = new String("hashCode");
        StringBuilder sb1 = new StringBuilder(s1);
        System.out.println("hashCode2: " + s1.hashCode() + " " + sb1.hashCode());
         
        // are they equals?
        System.out.println("s  s1 : " + s.equals(s1));
        System.out.println("sb sb1: " + sb.equals(sb1));
    }
}

 

   run 一下,能夠在控制檯看到:

hashCode1: 147696667 1385112968
hashCode2: 147696667 870919696
s  s1 : true
sb sb1: false

 

   泥瓦匠小結

一、s 與 s1相等,且hashCode同樣。驗證了【hashCode的常規協定】的第二條。緣由是字符串的散列碼由內容導出的。(這個第二個例子咱們會驗證)

二、StringBuilder 裏面沒有定義hashCode方法,因此導出的是Object默認的對對象存儲的地址。(注意到Object的hashCode方法前面有個native的修飾符,這表示hashCode方法是由非java語言實現的,具體的方法實如今外部,返回內存對象的地址。)詳情請看認識&理解關鍵字 native 實戰篇

    泥瓦匠剛剛提到字符串散列碼是由內容導出的。下面看看String的hashCode的實現。

 /** The value is used for character storage */private char value[];
 
private int hash;// Default to 0
 
/**
 * Returns a hash code for this string. The hash code for a
 * String object is computed as
 * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 */
public int hashCode()
{
    int h = hash;
    if (h == 0 && value.length > 0)
    {
        char val[] = value;
         
        for (int i = 0; i < value.length; i++)
        {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

 

    泥瓦匠小結

一、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<<5)-i。如今的VM能夠自動完成這種優化。

4、結論和忠告

確實hashCode有點晦澀,有多是由於那個數學散列函數。下面是《Effective Java》中的結論點:

一、若是對象有相同的散列碼,被映射到同一個散列桶,這樣散列表退化稱爲 鏈表 ,這樣性能下降。

二、相等的對象必須具備相等的散列碼

三、爲不相等的對象產生不相等的散列碼

四、不要試圖從散列碼計算中排除掉一個對象關鍵部分來提升性能

Writer:BYSocket(泥沙磚瓦漿木匠)

微博:BYSocket

豆瓣:BYSocket

相關文章
相關標籤/搜索