淺談HashMap的內部實現,淺談HashMap實現

來源:互聯網
上載者:User

淺談HashMap的內部實現,淺談HashMap實現

權衡時空

 

HashMap是以索引值對的方式儲存資料的。

 

如果沒有記憶體限制,那我直接用雜湊Map的鍵作為數組的索引,取的時候直接按索引get就行了,可是地價那麼貴,哪裡有無限制的地盤呢。

 

如果沒有時間限制的話,我可以把資料放到一個無序數組中,按順序尋找,遲早也能找到。可是time is money,光陰那麼短暫,誰又等得起呢。

 

所以,HashMap做了個折中的策略,使用適當的時間和空間做出了權衡,具體可以歸結為“鏈表散列法”,這是一個hash表處理衝突的經典方法。

   

  鏈表散列

 

那麼什麼是”鏈表散列法”呢?看:

 

 

 

縱向的是一個數組,數組的每一項都是一個鏈表。你可以把這個數組看成是N個桶,每一個桶放著一個鏈子。

 

數組是幹嘛的?數組的每一項負責放鏈表的。

 

鏈表是幹嘛的?負責放Map資料的,比如一個HashMap有兩個鍵,一個是key1,一個是key2。那麼該鏈表就會分出兩個節點分別存放這兩個索引值對(每一個索引值對是打包放在Entry對象中的)。

 

鏈表是怎麼鏈起來的?Entry包含有key、value、下一個節點next、hash值等,這個next就把各個節點串了起來。

 

HashMap儲存資料的過程為:先計算當前要儲存的索引值對的雜湊值(決定著當前索引值對要放到哪個桶中),根據這個雜湊值找到對應的桶。如果桶中沒有資料,那就直接放進去。如果桶中已經放了資料(也即:桶中的鏈條上放著一個或者多個索引值對),那就順著桶中的這個鏈條一個一個比對,看有沒有key與當前要儲存的資料的key相同。如果有相同,直接覆蓋原來key的value。如果沒有相同的,那麼將該元素儲存在鏈頭(最早儲存的元素就會跑到鏈尾)。

   

  裝填因子

 

桶的數量決定了能放多少個HashMap,而具體用了多少個桶,則直接關係著尋找的效率。打個比方,你去隔壁班找小明,班裡有10個人,你很快就會找到小明,班裡坐著100個人,你可能找半天才能找到。所以你去看HashMap的建構函式是這樣的:

 

public HashMap(int initialCapacity, float loadFactor) {        if (initialCapacity < 0)            throw new IllegalArgumentException("Illegal initial capacity: " +                                               initialCapacity);        if (initialCapacity > MAXIMUM_CAPACITY)            initialCapacity = MAXIMUM_CAPACITY;        if (loadFactor <= 0 || Float.isNaN(loadFactor))            throw new IllegalArgumentException("Illegal load factor: " +                                               loadFactor);        this.loadFactor = loadFactor;        threshold = initialCapacity;        init();    }public HashMap(int initialCapacity) {        this(initialCapacity, DEFAULT_LOAD_FACTOR);    }public HashMap() {        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);}

 

三個建構函式都牽動著兩個東西:initialCapacity,loadFactor。前者表示的是桶的初始數量(即數組大小),後者表示“裝填因子”,裝填因子是雜湊表在其容量自動增加之前可以達到多滿的一種尺度。比如,數組初始大小為100,如果裝填因子=0.6,表示當數組中存放了60個Map之後,就要把數組擴容後才能繼續存放。這就是為瞭解決上面講到的效率問題。

 

裝填因子定的小了,尋找資料就快些,但是浪費空間。裝填因子大了,空間利用率就高,但是浪費時間。生活就是這樣,顧此失彼在所難免,萬事哪有兩全的呢。系統權衡利弊後,預設給的裝填因子是0.75,這個一般我們是不需要改動的。

   

  除留餘數

 

那麼還有個問題。拿到一個Map的雜湊值,怎麼決定放到哪個桶裡呢?如果最後數組中的Map資料都擠到一塊兒那可不行,查詢就會慢。太鬆了也不行,浪費空間。Java用了一招“除留餘數法”,保證資料在數組中分布均勻。

 

“除留餘數法”,就是模數。比如數組的長度是100,Map的雜湊值是80,用80%100,餘數是80,就放到80那個位置。但是Java可不是那樣幹算的呦,且看源碼:

 

void addEntry(int hash, K key, V value, int bucketIndex) {        if ((size >= threshold) && (null != table[bucketIndex])) {            resize(2 * table.length);            hash = (null != key) ? hash(key) : 0;            bucketIndex = indexFor(hash, table.length);        }        createEntry(hash, key, value, bucketIndex);}

 

上面代碼就是HashMap中的添加Entry資料的方法。BucketIndex就是當前Map在數組中的索引。第三行擴容且不談,重點在indexFor方法,這個方法就是”模數”。我們點進去看一下:

 

static int indexFor(int h, int length) {// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";        return h & (length-1);}

 

H是Map的雜湊值,length是數組的長度。它直接使用了一個h & (length - 1)。這一句其實就相當於對數組模數,但是直接用二進位的位操作,比數學計算要快的多。這也給了我們程式員一個啟發,能用位元運算時盡量用,提高逼格又提高效率。

   

  均勻分布

 

還有個有趣的地方,上面代碼的注釋部分:length must be a non-zero power of 2,這句是說,數組的長度必須是2的n次方。

 

為啥是2的n次方呢?

 

如果不是2的n次方,比如length為15,h分別為2,3,4。那麼用h & (length -1)有:

 

h

Length-1

h & (length -1)

0010

1110

0010,即2

0011

1110

0010,即2

0100

1110

0100,即4

 

你看,隨便測了三個數字,就發生了碰撞。為什麼會這樣呢?

 

這是因為:如果不是2的n次方,那麼2^n – 1的最低位必然為0,而0、1分別和0作“與”運算,結果都為0。也就是說,不論h為多少,h & (length - 1)的結果最低位都是0。那麼數組中最低位為1的那些位置就全部空缺著,這就導致資料在數組中分布不均勻了,繼而影響了查詢的效率。

 

讀取資料的時候就簡單多了,通過key的hash值找到在table數組中的索引處的Entry,然後返回該key對應的value即可。

 

參考資料:

 

http://www.cnblogs.com/chenssy/p/3521565.html

http://blog.csdn.net/zhuanshenweiliu/article/details/39177447

http://blog.csdn.net/tanggao1314/article/details/51457585#t1

http://www.importnew.com/18851.html

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.