1. 程式人生 > >HashMap中的為什麼hash的長度為2的冪而&位必須為奇數

HashMap中的為什麼hash的長度為2的冪而&位必須為奇數

背景

雜湊演算法在Java中是經常用的的一個演算法,也是一些常用資料結構中必用的一個演算法,它為上層的複雜資料結構提供了基礎支撐。

雜湊演算法的實現有很多種,除了這裡講的map中的hashcode演算法,還有其他雜湊演算法:

1.直接定址法
2.數字分析法
3.摺疊法
4.平方取中法
5.減去法
6.字串數值雜湊法
7.旋轉法

更多演算法請參考另一篇作者的文章:
hash演算法原理詳解

HashMap中的HashCode演算法詳解

1,雜湊演算法在HashMap類中的應用
java中的集合,比如HashMap/Hashtable/HashSet等,在實現時,都用到了雜湊演算法。當我們向容器中新增元素時,我們有必要知道
這個元素是否已經存在。
從實現上來說,java是藉助hashcode()方法和equals()方法來實現判斷元素是否已經存在的。當我們向HashMap中插入元素A時,首先,
呼叫hashcode()方法,判斷元素A在容器中是否已經存在。如果A元素的hashcode值在HashMap中不存在,則直接插入。否則,接著呼叫
equals()方法,判斷A元素在容器中是否已經存在。hashcode()的時間複雜度為O(1),equals()方法的時間複雜度為O(m),整體的時間複雜度
就是:O(1) + O(m)。其中,m是桶的深度。桶的深度是一個什麼概念呢,桶的深度是指具有相同hashcode值得元素的個數,也就是發生雜湊
碰撞的元素的個數。
一個好的雜湊演算法應該儘量減少雜湊碰撞的次數。

HashCode是Object中本身就有的方法,但是沒有具體實現。而各個不同的資料型別又繼承實現了各自的具體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類的hashcode值(雜湊值)是如何計算得到的?具體實現?為了方便閱讀,我們來進行分步說明

static void hashcodeTest(){  

    String str = "yangcq";  

    // 第一步 = (int)'y'  
    // 第二步 = (31 * (int)'y') + (int)'a'  
    // 第三步 = 31 * ((31 * (int)'y') + (int)'a') + (int)'n'  
    // 第四步 = 31 * (31
* ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g' // 第五步 = 31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c' // 第六步 = 31 * (31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c') + (int)'q' // 上面的過程,也可以用下面的方式表示 // 第一步 = (int)'y' // 第二步 = 31 * (第一步的計算結果) + (int)'a' // 第三步 = 31 * (第二步的計算結果) + (int)'n' // 第四步 = 31 * (第三步的計算結果) + (int)'g' // 第五步 = 31 * (第四步的計算結果) + (int)'c' // 第六步 = 31 * (第五步的計算結果) + (int)'q' int hashcode = 31 * (31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c') + (int)'q'; System.out.println("yangcq的hashcode = " + hashcode); // yangcq的hashcode = -737879313 System.out.println("yangcq的hashcode = " + str.hashCode()); // yangcq的hashcode = -737879313 }

為什麼HashMap中的&位必須為奇數(Length - 1)

從Key對映到HashMap陣列的對應位置,會用到一個Hash函式:

index = Hash(“apple”)

如何實現一個儘量均勻分佈的Hash函式呢?我們通過利用Key的HashCode值來做某種運算。
如何進行位運算呢?有如下的公式(Length是HashMap的長度):

index = HashCode(Key) & (Length - 1)

下面我們以“book”的Key來演示整個過程:

1.計算book的hashcode,結果為十進位制的3029737,二進位制的101110001110101110 1001。

2.假定HashMap長度是預設的16,計算Length-1的結果為十進位制的15,二進位制的1111。

3.把以上兩個結果做與運算,101110001110101110 1001 & 1111 = 1001,十進位制是9,所以 index=9。

可以說,Hash演算法最終得到的index結果,完全取決於Key的Hashcode值的最後幾位。

假設HashMap的長度是10,重複剛才的運算步驟:
這裡寫圖片描述

單獨看這個結果,表面上並沒有問題。我們再來嘗試一個新的HashCode 101110001110101110 1011 :
這裡寫圖片描述
讓我們再換一個HashCode 101110001110101110 1111 試試 :
這裡寫圖片描述

是的,雖然HashCode的倒數第二第三位從0變成了1,但是運算的結果都是1001。也就是說,當HashMap長度為10的時候,有些index結果的出現機率會更大,而有些index結果永遠不會出現(比如0111)!

這樣,顯然不符合Hash演算法均勻分佈的原則。

反觀長度16或者其他2的冪,Length-1的值是所有二進位制位全為1,這種情況下,index的結果等同於HashCode後幾位的值。只要輸入的HashCode本身分佈均勻,Hash演算法的結果就是均勻的。

相關推薦

HashMap的為什麼hash長度2&必須奇數

背景 雜湊演算法在Java中是經常用的的一個演算法,也是一些常用資料結構中必用的一個演算法,它為上層的複雜資料結構提供了基礎支撐。 雜湊演算法的實現有很多種,除了這裡講的map中的hashcode演算法,還有其他雜湊演算法: 1.直接定址法 2.數字分析

abap檢查字串長度、字串是否有空格、前兩是否字元、正則表示式

IF i_mseg-charg <> ''.  "字串長度   CLEAR lv_len.   lv_len = strlen( i_mseg-charg ).   IF lv_len < 8.     MESSAGE e005(zmigo).   END

hashmap hash函式h & (length-1)詳解

Hashmap是一種非常常用的、應用廣泛的資料型別,最近研究到相關的內容,就正好複習一下。網上關於hashmap的文章很多,但到底是自己學習的總結,就發出來跟大家一起分享,一起討論。  1、hashmap的資料結構  要知道hashmap是什麼,首先要搞清楚它的資料結構,在

在JAVA,把資料插入資料庫欄型別DATA方法

方法1: /***********獲取系統當前時間***************/ Date nowTime = new Date(System.currentTimeMillis()); SimpleDateFormat bartDateFormat = new Si

MySQL空的欄設定NULL還是NOT NULL

 Bruin  2個月前 (06-23)  316瀏覽  0評論 經常用mysql的人可能會遇到下面幾種情況: 1、我欄位型別是not null,為什麼我可以插入空值 2、為什麼not null的效率比null高 3、判斷欄位不為空的時候,到底要用 selec

為什麼Hash函式 H(k) = k % m m 儘量不要2次 也不是要是2^i -1

為什麼Hash函式 H(k) = k % m中 m 儘量不要為2的冪次 下面的截圖來自CLRS的11章 關於雜湊函式的討論 之前我就一直困惑,為什麼             When usin

JDK1.7HashTable的hash為什麼對素數求餘,不像HashMap一樣對2的N次方求餘?

常用的hash函式是選一個數m取模(餘數),這個數在課本中推薦m是素數,但是經常見到選擇m=2^n,因為對2^n求餘數更快,並認為在key分佈均勻的情況下,key%m也是在[0,m-1]區間均勻分佈的。但實際上,key%m的分佈同m是有關的。證明如下:key%m = key - xm,即key減掉

(轉)什麽HashMap鏈表長度超過8會轉換成紅黑樹

假設 n) net hashmap 頻繁 發生 等於 pan tails 原博地址:https://blog.csdn.net/xingfei_work/article/details/79637878 HashMap在jdk1.8之後引入了紅黑樹的概念,表示若桶中鏈表

HashMap 容量2的原因

我們都知道 hashmap 的底層是一個數組加連結串列的結構,當向其中新增一個元素的時候,需要根據key的hash值,去確定其在陣列中的具體位置。 看原始碼,我們可以發現,確定陣列位置的實現是 i=(n-1)& hash,其中 n 代表陣列的長度,即map的容量。 當n為2的冪

從撲克牌隨機抽5張排,判斷是不是一個順子,即這5張牌是不是連續的。2~10數字本身,A 1,J11,Q12,K13,大,小王可以看成任意數字。

#define  _CRT_SECURE_NO_WARNINGS   #include<stdio.h>   #include<math.h.>   #include<assert.h>   #define n 5   int main()   {    

HashMap長度為什麼設定2的n次方

1. 建構函式中控制長度必須為2的n次方. 首先在構造方法中, 有下面這段程式碼, 其中initialCapacity是我們傳入的自定義map容量大小(如果不設定, 預設是16) 如果我們自

什麼是 雜湊表 HashMap 陣列的 size 為什麼必須2 的整數次

Hash,一般翻譯做“雜湊”,也有直接音譯為“雜湊”的,就是把任意長度的輸入(又叫做預對映, pre-image),通過雜湊演算法,變換成固定長度的輸出,該輸出就是雜湊值。這種轉換是一種壓縮對映,也就是,雜湊值的空間通常遠小於輸入的空間,不同的輸入可能會雜湊成相同的輸出,

判別無向圖任意給定的2個頂點之間是否存在一條長度 k的簡單路徑

判別無向圖中任意給定的2個頂點之間是否存在一條長度 為k的簡單路徑  無向圖是沒有權值的這裡的k值代表經歷k-1個頂點 不要以為很簡單,裡面很niu的  int visited[MAXSIZE] /

CC2500RGPR是一種低成本單片2.4G 收發器,低功耗無線應用設計

5.6 應用電路 -1 健康 低功耗 電路 分銷商 價格 國內 CC2500RGPR是一種低成本單片2.4G 收發器,為低功耗無線應用而設計。2400~2483.5MHz ISM及SRD頻率波段。高度集成的RF收發芯片,可以配置基帶調制解調,最高速率可以到500Kbps。

MysqlENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8怎麽轉換sql sever2008的代碼

需要 引擎 mysq ide fault inno char 數據 排序規則 ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8轉換sql server AUTO_INCREMENT=2 ,是 自動遞增列的 初始數值 =

什麽Java1000==1000false100==100true?

png 需要 ice 價值 java技術 poi 實例 via 占用 為什麽Java中1000==1000為false而100==100為true? 這是一個挺有意思的討論話題。 如果你運行下面的代碼: 基本知識:我們知道,如果兩個引用指向同一個

最近項目遇到了一個場景,其實很常見,就是定時獲取接口刷新數據。那麽問題來了,假設我設置的定時時間1s,數據接口返回大於1s,應該用同步阻塞還是異步?

set timeout pre git plain 異步執行 項目 strip nod 初識setTimeout 與 setInterval 先來簡單認識,後面我們試試用setTimeout 實現 setInterval 的功能 setTimeout 延遲一段時間執行一

java自定義類型 作為HashMap的Key值 (Pair<V,K>例)

由於 con als void hash system 進行 原型 自定義 由於是自定義類型,所以HashMap中的equals()函數和hashCode()函數都需要自定義覆蓋。 不然內容相同的對象對應的hashCode會不同,無法發揮算法的正常功能,覆蓋equals函

HashMap推薦使用entrySet方式遍歷Map類集合KV不是keySet方式遍歷

HashMap中EntrySet和KeySet的比較 前言 閱讀《阿里巴巴Java開發手冊終極版v1.3.0》時,看到如下一句話:   【推薦】使用entrySet遍歷Map類集合KV,而不是keySet方式進行遍歷。   說明:keySet其實是

什麽js0.1+0.2不等於0.3,怎樣處理使之相等?(轉載)

number 就會 理解 als 轉載 解決 面試 精度 超過 為什麽js中0.1+0.2不等於0.3,怎樣處理使之相等? console.log(0.1+0.2===0.3)// true or false?? 在正常的數學邏輯思維中,0.1+0.2=0.3這個邏輯是正確