something_about_hashCode

(注 : 此blog主要是爲了加深java 源碼的認知度的記錄java

hashCode是一個返回hash(散列碼)的方法,hash 就是用於區分對象的標誌,就是相似於人類的基因,咱們的母類Object 就擁有這樣的hashCode方法來返回hash值,這個在java 集合類的Map中是核心,因此map玩的溜,就得搞清楚鍵值對,鍵值對的重點就是hash算法

瞭解了hash以後,咱們再來看看java 類庫中的一個比較特殊的基本數據類型 String ui

先來看看代碼this

 

 1 /**
 2  *  hashCode
 3  */
 4 package test;
 5 
 6 /**
 7  * @author Amory.Wang
 8  * Question : 
 9  * 2017年9月4日下午7:18:35
10  */
11 public class Test {
12     public static void main(String[] args) {
13         String s = new String("s");
14         String t = new String("s");
15         
16         System.out.println(s.hashCode() == t.hashCode());
17         System.out.println(s.hashCode());
18         System.out.println(t.hashCode());
19     }
20 }

 

第一個syso 打印的是什麼?spa

有點java 基礎的猿都應該知道s 和 t 是兩個不一樣的對象,這一點的證實能夠用 == 來打印可看,因此答案應該是false,可是然而,倒是true 翻譯

你可能會反問 :wtf?你不是說不一樣的對象有本身的hash嗎,本身的基因嗎,你在欺騙我嗎?code

其實否則,因此看看源碼就知道,String 裏面是實現了hashCode()的方法的, 附碼以下:對象

 1 /**
 2      * Returns a hash code for this string. The hash code for a
 3      * <code>String</code> object is computed as
 4      * <blockquote><pre>
 5      * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 6      * </pre></blockquote>
 7      * using <code>int</code> arithmetic, where <code>s[i]</code> is the
 8      * <i>i</i>th character of the string, <code>n</code> is the length of
 9      * the string, and <code>^</code> indicates exponentiation.
10      * (The hash value of the empty string is zero.)
11      *
12      * @return  a hash code value for this object.
13      */
14     public int hashCode() {
15         int h = hash;
16         if (h == 0 && value.length > 0) {
17             char val[] = value;
18 
19             for (int i = 0; i < value.length; i++) {
20                 h = 31 * h + val[i];
21             }
22             hash = h;
23         }
24         return h;
25     }

 

我標記的紅色重點看到沒有 ?! 先解釋一下 在String 類中定義的hash  和 value 以下 : blog

/** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

hash 的默認是 0 , value 是建立對象的時候輸入的參數,因此上面就是 String 類中產生hash值的方法,再來讀一讀這個源碼方法,很明顯能夠看出,hash主要的值是有val的值決定的,源碼

h = 31 * h + val[i];

而val 就是咱們傳入的參數嘛,這下清楚了吧,也就是說,其實String的hashCode返回的hash值直接關鍵就是咱們傳入的參數值,參數值同樣,ok,產生的hash就是同樣的。

愛問問題的人(好比我,哈哈,開個玩笑)可能會問,這個hash的生成前面不是還有一個乘法的操做嗎?並且乍一看尚未意義,由於定義了h默認爲0,乘起來就是0啊?

嗯,java組織畢竟都是大神,怎麼會容忍沒有意義的代碼,這裏其實要涉及到了計算機基礎的知識,關於乘法的操做,(簡單的講一下啊,畢竟要深刻那可仍是一本書的知識) 計算機的5大部件之一,運算器,(CPU是由運算器和控制器組成的)就是執行運算功能的,尚未智能到能夠想人腦同樣來計算乘法操做,而是由減法或者邏輯運算來代替的,因此這裏的乘法,再翻譯一下就是 這樣的

31 * h == (h << 5) - h

爲何要作這個看上去畫蛇添足的動做?具體本身百度,簡單說就是這樣產生的hash值衝突不會不少,更加容易定位。

若是你使用 31,33, 37,39 和 41 這幾個數值,將其應用於 hashCode 的算法中,每個數字對超過 50000 個英語單詞(由兩個 Unix 版本的字典的並集構成)產生的 hash 只會產生少於 7 個的衝突。知道了這個以後,Java 大多數的發行版均會使用這幾個數值之一的事實對你也不會顯得奇怪了。


某乎上覆制的。。。

 

再來看下面的代碼

/**
 *  hashCode
 */
package test;

/**
 * @author Amory.Wang
 * Question : 
 * 2017年9月4日下午7:18:35
 */
public class Test {
    public static void main(String[] args) {
        String s = new String("s");
        String t = new String("s");
        
        System.out.println(s.hashCode() == t.hashCode());
        System.out.println(s.hashCode());
        System.out.println(t.hashCode());
        
        StringBuilder sb = new StringBuilder("s");
        StringBuilder sb1 = new StringBuilder("s");
        System.out.println(sb.hashCode() == sb1.hashCode()); System.out.println(sb.hashCode()); System.out.println(sb1.hashCode());
    }
}

這個sb 是否是相等的呢?stringBuilder是否是和String 同樣的呢?答案顯然是 不是的,由於StringBuilder裏面沒有重寫hashCode的方法,因此直接調用的是咱們偉大的母類的Object的hashCode的方法,這裏面的實現就是根據對象儲存的位置有關,你家在哪,你的hash就是啥,一樣,通常的對象,也是如此,因此通常的對象hash都是不同的,總結一下String 類就是想搞特殊。。。開個玩笑

本站公眾號
   歡迎關注本站公眾號,獲取更多信息