[回到起點]看看Java HashMap的源碼和實現

  剛出來工作的時候,老大要我從文本提取一些數據, 篩選排序什麼的,那時我只曉得用ArrayList, 結果事情是做完了,執行卻有點慢。老大看代碼說我是在寫C語言, 讓我試試用HashMap處理, 結果是哇,快好多,從此對HashMap很有好感。年復一年的面試, HashTable,HashMap,TreeMap有啥區別啊,爲什麼那麼快啊之類, 每個工程師都會在面試中得到鍛鍊和成長:p。面試受打擊了,可能網上一搜這個面試題,喔原來答案是這個。真去看下源碼的人估計不多,老實說筆者最近才仔細拜讀-_-,結合搜索的分享的文章,結果發現自己的源碼太新了:p JDK1.8的,大多文章還是JDK1.7的,突然想起看過一篇文章關於JDK重寫了HashMap爲了解決哈希碰撞的事。JDK能對已有老的庫能升級也是蠻罕見的事,所以就有了這篇文章。

  •   先看JDK1.7的HashMap爲什麼快?

  HashMap使用Entry<K,V>[] table保存數據, 每個節點Entry.next指向另外Entry節點, next存在則表示有相同的key哈希, JDK1.7使用鏈表保存解決衝突。

    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
  HashMap查找快關鍵的因素是key hashcode在table數組有一個對應的下標。indexFor是精心設計過的,table.length必須爲2的整數次冪, 即length-1爲奇數, 奇數做位與纔可能產生奇數或偶數(偶數位與就都是偶數了), 保證Entry在table數組中的均勻分佈。

    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[<span style="color:#FF0000;">indexFor</span>(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }
    /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        //<span style="color:#FF0000;"> assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"</span>;
        return <span style="color:#FF0000;">h & (length-1)</span>;
    }
  話說redis,php哈希採用了類似的數據結構, 可能redis的哈希算法可能衝突會小些。

  那哈希衝突會帶來什麼問題呢?假設我們寫個POJO, hashCode()總是返回0,這個POJO作爲HashMap key值的就都衝突了,所有的Entry就退化爲鏈表了,查找就是鏈表的線性效率了,有人惡意造數據造成了衝突,系統查找響應就很慢, 導致DOS拒絕服務。所以JDK1.8就稍微優化了下。


  • JDK1.8 HashMap改進

當哈希衝突的鏈表達到一定長度(好像是8),就改用紅黑樹解決保存節點,就算衝突多查找也不會太慢。


虎頭蛇尾。。。下班了。。see you very soon。


Cheers



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