HashMap,哈希表,哈希方法

本篇文章轉載

http://www.dnbcw.com/biancheng/java/prek254784.html

 

http://www.javaeye.com/topic/539465

 

本文以自己的理解方式整理上述2篇文章,以便閱讀。

 

hashmap的數據結構

要知道hashmap是什麼,首先要搞清楚它的數據結構,在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,hashmap也不例外。Hashmap實際上是一個數組和鏈表的結合體(在數據結構中,一般稱之爲“鏈表散列“),請看下圖(橫排表示數組,縱排表示數組元素【實際上是一個鏈表】)。 

     當我們往hashmap中put元素的時候,先根據key的hash值得到這個元素在數組中的位置(即下標),然後就可以把這個元素放到對應的位置中了。如果這個元素所在的位子上已經存放有其他元素了,那麼在同一個位子上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。從hashmap中get元素時,首先計算key的hashcode,找到數組中對應位置的某一元素,然後通過key的equals方法在對應位置的鏈表中找到需要的元素。從這裏我們可以想象得到,如果每個位置上的鏈表只有一個元素,那麼hashmap的get效率將是最高的,但是理想總是美好的,現實總是有困難需要我們去克服,哈哈~

     我們可以看到在hashmap中要找到某個元素,需要根據key的hash值來求得對應數組中的位置。如何計算這個位置就是hash算法。前面說過hashmap的數據結構是數組和鏈表的結合,所以我們當然希望這個hashmap裏面的元素位置儘量的分佈均勻些,儘量使得每個位置上的元素數量只有一個,那麼當我們用hash算法求得這個位置的時候,馬上就可以知道對應位置的元素就是我們要的,而不用再去遍歷鏈表

 

 

哈希表與哈希方法

       哈希方法在“鍵- 值對”的存儲位置與它的鍵之間建立一個確定的對應函數關係 hash() ,使得每一個鍵與結構中的一個唯一的存儲位置相對應:存儲位置=hash( 鍵 )

       在搜索時,首先對鍵進行hash 運算,把求得的值當做“鍵 - 值對”的存儲位置,在結構中按照此位置取“鍵 - 值對”進行比較,若鍵相等,則表示搜索成功。

      在存儲“鍵 - 值對”的時候,依照相同的 hash 函數計算存儲位置,並按此位置存放,這種方法就叫做哈希方法,也叫做散列方法。在哈希方法中使用的轉換函數 hash 被稱作哈希函數 ( 或者散列函數 ) 。按照此中算法構造出來的表叫做哈希表 ( 或者散列表 ) 。

      Hash函數處理流程,一般翻譯做"散列",也有直接音譯爲"哈希"的,就是把任意長度的輸入(又叫做預映射, pre-image),通過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不同的輸入可能會散列成相同的輸出,而不可能從散列值來唯一的確定輸入值。簡單的說就是一種將任意內容的輸入轉換成相同長度輸出的加密方式.

      哈希函數建立了從“鍵- 值對”到哈希表地址集合的一個映射,有了哈希函數,我們就可以根據鍵來確定“鍵 - 值對”在哈希表中的位置的地址。使用這種方法由於不必進行多次鍵的比較,所以其搜索速度非常快,很多系統都使用這種方法進行數據的組織和檢索。

舉一個例子,有一組“鍵值對”:

        <5, ” tom ” >

        <8, ” Jane ” >

        <12, ” Bit ” >

        <17, ” Lily ” >

         <20, ” sunny ” >

      我們按照如下哈希函數對鍵進行計算 :hash(x)=x%17+3 ,得出如下結果:

       <5, ” tom ” > ----------hash(5)=8 

        <8, ” Jane ” >---------hash(8)=11 

        <12, ” Bit ” >---------- hash(12)=15 

        <17, ” Lily ” >----------hash(17)=3 

         <20, ” sunny ” >------hash(20)=6 

我們把 <5, ” tom ” >、 <8, ” Jane ” >、 <12, ” Bit ” >、 <17, ” Lily ” >、 <20, ” sunny ” >分別放到地址爲 8 、 11 、 15 、 3 、 6 的位置上。當要檢索 17 對應的值的時候,只要首先計算 17 的哈希值爲 3 ,然後到地址爲 3 的地方去取數據就可以找到 17 對應的數據是“ Lily ”了,可見檢索速度是非常快的。

衝突與衝突的解決

     通常鍵的取值範圍比哈希表地址集合大很多,因此有可能經過同一哈希函數的計算,把不同的鍵映射到了同一個地址上面,這就叫衝突。比如,有一組“鍵- 值對”,其鍵分別爲 12361 、 7251 、 3309 、 30976 ,採用的哈希函數是:

        public static int hash(int key){

              return key%73+13420;

        }

則將會得到hash(12361)=hash(7251)=hash(3309)=hash(30976)=13444 ,即不同的鍵通過哈希函數對應到了同一個地址,我們稱這種哈希計算結果相同的不同鍵爲同義詞。

       如果“鍵- 值 對”在加入哈希表的時候產生了衝突,就必須找另外一個地方來存放它,衝突太多會降低數據插入和搜索的效率,因此希望能找到一個不容易產生衝突的函數,即構 造一個地址分佈比較均勻的哈希函數。常用的哈希函數包括:直接定址法、數字分析法、除留餘數法、乘留餘數法、平方取中法、摺疊法等。應該根據實際工作中關 鍵碼的特點選用適當的方法。

       雖然採用合適的哈希方法能夠降低衝突的概率,但是衝突仍然是不可避免的,處理衝突的最常用方法就是“桶”算法:假設哈希表有m 個地址,就將其改爲 m 個“桶”,其桶號與哈希地址一一對應,每個桶都用來存放互爲同義詞的鍵,也就是如果兩個不同的鍵用哈希函數計算得到了同一個哈希地址,就將它們放到同一個桶中,檢索的時候在桶內進行順序檢索。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章