1. 程式人生 > >hash演算法 (hashmap 實現原理)

hash演算法 (hashmap 實現原理)

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

HASH 主要用於資訊保安領域中加密演算法,它把一些不同長度的資訊轉化成雜亂的128 位的編碼, 這些編碼值叫做HASH 值. 也可以說,hash 就是找到一種資料內容和資料存放地址之間的對映關係

例如字串 hello 的雜湊演算法

char* value = "hello"; int key = (((((((27* (int)'h'+27)* (int)'e') + 27)  * (int)'l') + 27) * (int)'l' +27) * 27 ) + (int)'o' ; 。

陣列的特點是:定址容易,插入和刪除困難;而連結串列的特點是:定址困難,插入和刪除容易。那麼我們能不能綜合兩者的特性,做出一種定址容易,插入刪除也容易的資料結構?答案是肯定的,這就是我們要提起的雜湊表,雜湊表有多種不同的實現方法,我接下來解釋的是最常用的一種方法—— 拉鍊法,我們可以理解為“ 連結串列 的陣列” ,如圖:

HashMap其實也是一個線性的陣列實現的,所以可以理解為其儲存資料的容器就是一個線性陣列。這可能讓我們很不解,一個線性的陣列怎麼實現按鍵值對來存取資料呢?這裡HashMap有做一些處理。

1.首先HashMap裡面實現一個靜態內部類Entry 其重要的屬性有 key , value, next,從屬性key,value我們就能很明顯的看出來Entry就是HashMap鍵值對實現的一個基礎bean,我們上面說到HashMap的基礎就是一個線性陣列,這個陣列就是Entry[],Map裡面的內容都儲存在Entry[]裡面。

2.既然是線性陣列,為什麼能隨機存取?這裡HashMap用了一個小演算法,大致是這樣實現:

複製程式碼

儲存時:

int hash = key.hashCode();--> 這個hashCode方法這裡不詳述,只要理解每個key的hash是一個固定的int值

int index = hash % Entry[].length;

Entry[index] = value;

取值時:

int hash = key.hashCode();

int index = hash % Entry[].length;

return Entry[index]

複製程式碼

到這裡我們輕鬆的理解了HashMap通過鍵值對實現存取的基本原理

3.疑問:如果兩個key通過hash % Entry[].length得到的index相同,會不會有覆蓋的危險?

這裡HashMap裡面用到鏈式資料結構的一個概念.上面我們提到過Entry類裡面有一個next屬性,作用是指向下一個Entry。打個比方, 第一個鍵值對A進來,通過計算其key的hash得到的index=0,記做:Entry[0] = A.一會後又進來一個鍵值對B,通過計算其index也等於0,現在怎麼辦?HashMap會這樣做:B.next = A,Entry[0] = B,如果又進來C,index也等於0,那麼C.next = B,Entry[0] = C;這樣我們發現index=0的地方其實存取了A,B,C三個鍵值對,他們通過next這個屬性連結在一起。所以疑問不用擔心。

到這裡為止,HashMap的大致實現,我們應該已經清楚了。

當然HashMap裡面也包含一些優化方面的實現,這裡也囉嗦一下。

比如:Entry[]的長度一定後,隨著map裡面資料的越來越長,這樣同一個index的鏈就會很長,會不會影響效能?

HashMap裡面設定一個因素(也稱為因子),隨著map的size越來越大,Entry[]會以一定的規則加長長度。

解決hash衝突的辦法

1)開放定址法(線性探測再雜湊,二次探測再雜湊,偽隨機探測再雜湊)

2)再雜湊法

3)鏈地址法

4)建立一 公共溢位區

java 中hashmap的解決辦法就是採用的鏈地址法