1. 程式人生 > >HashMap為什麼這麼快? ---深入理解HashMap的雜湊機制

HashMap為什麼這麼快? ---深入理解HashMap的雜湊機制

在通過上篇部落格瞭解了 Map 的維持內部的 鍵-值 對的基本方式之後,----不瞭解的看這篇(Java集合的基本概括), 我們便會思考, 在 HashMap 內部是如何組織和排列這些封裝了 鍵-值對的 Map.Entry 實體呢? 如何組織才能達到更高的效率呢?

在瞭解 HashMap 的雜湊機制之前, 我們不妨來假設一下 HashMap 內部的資料結構的排列方式.

  • 通過陣列來維持內部的 鍵-值 對.

陣列也許是我們最容易想到的一種資料結構, 我們知道, 儲存一組資料最快的資料結構便是陣列,這也是陣列僅存的優點, 同樣陣列的缺陷也很明顯, 比如 需要大量的連續的記憶體空間, 對元素的插入操作代價高昂,不能隨意的擴大容量....等等.

因此,如果在 HashMap 的內部通過陣列來維持 鍵-值 對, 在我們增加元素的時候似乎開銷不大, 但是當我們用一個 key 去獲取一個 value的時候, 想想我們該怎麼去實現, 最常見的方式便是對 key 進行線性的 equals 查詢, 從頭查到尾, 這樣的查詢速度如此低下, 效率可想而知, 還有一個缺陷便就是陣列與生俱來的, 需要太大的連續的記憶體空間, 所以通過陣列來維持 HashMap 內部的資料缺陷太大,不值得.

  • 通過連結串列來維持

在上面說過陣列的那麼多缺陷之後, 可能很多同學便會說到用連結串列, 沒錯連結串列似乎是解決了陣列一個最致命的缺陷(需要大量的連續的記憶體空間), 但是連結串列仍然無法解決當我們用一個 key 去查詢一個 value 的時候的低下的效率, 原因仍然是從頭到尾的線性遍歷.....

在上面我們說到用陣列或者連結串列來維護內部的 鍵-值 對似乎效率都很低下,有沒有一種能夠結合陣列和連結串列兩者各自的優點然後又有著高效的查詢速度的資料結構呢?

這便是 HashMap 內部維護資料的方式 --- 陣列連結串列+雜湊機制

為速度而雜湊:

雜湊的價值便在於速度, 雜湊使得查詢得以快速進行, 那麼什麼是陣列連結串列,什麼又是雜湊機制呢?

  • 正所謂陣列連結串列, 便是定義一個數組, 然後陣列的每一個成員都是一條連結串列,陣列只需要記載這條連結串列的引用即可, 這樣不需要直接在陣列內部儲存 鍵-值 對而需要大量的連續的記憶體空間.
  • 雜湊機制便是所謂的 hashCode 方法返回的 int 數, 他是通過物件的資訊(預設是地址),通過某種雜湊的數學函式生成的一串 int 數字.

在瞭解了陣列連結串列和雜湊機制後,我們再來想一想 HashMap 內部的 鍵-值 對是如何高效的維持的.

  • 首先, 仍然會定義一個數組, 這個陣列的是一個儲存連結串列的引用陣列, 從而解決了陣列因儲存物件而需要大量的連續的記憶體空間的缺陷.
  • 然後, 我們在 put 一個元素的時候, 會呼叫 key 的 HashCode 方法生成一個雜湊碼, 然後用這個雜湊碼餘上陣列的容量,從而得到了一個數組的下標, 然後把這個 鍵-值 對儲存在這個下標下對應的連結串列內.
  • 在理想的情況下, 假設沒有雜湊衝突(不同的物件產生了相同的雜湊碼), 在我們用 key 去查詢一個 value 的時候, 仍然用這個雜湊函式得到陣列的下標, 從而直接獲取了對應的 value, 這個效率簡直了.....提升了多少倍啊.....
  • 可是不會產生衝突的雜湊函式是幾乎不存在的, 於是乎便會出現不同的 key 產生了相同的雜湊碼, 在我們查詢的時候就得采用 equals 線性遍歷這少部分的因衝突而儲存在一個連結串列中的 鍵-值 對, 但是這和全部的元素進行線性遍歷, 效率仍然是提高了很多倍.

通過上面的解釋, 採用 陣列連結串列和雜湊機制 來實現的 HashMap為什麼效率這麼高的原因理解了, 下面來實現一個簡單的 HashMap.

   class MyHashMap<k, v> extends AbstractMap<k, v> {

        private static final int SIZE = 16;
        LinkedList<Map.Entry<k, v>>[] buckets = new LinkedList[SIZE];

        @Override
        public v put(k key, v value) {
            int index = key.hashCode() % SIZE;
            if (buckets[index] == null) {
                buckets[index] = new LinkedList<>();
            }

            boolean found = false;
            Map.Entry<k, v> pair = new HashMap.SimpleEntry<>(key, value);
            v oldValue = null;
            // Begin to found whether already have this key and value.
            for (Entry<k, v> kvEntry : buckets[index]) {
                // If already have this key, replace value.
                if (kvEntry.getKey().equals(key)) {
                    oldValue = kvEntry.getValue();
                    found = true;
                    buckets[index].set(buckets[index].indexOf(kvEntry), pair);
                    break;
                }
            }
            // If not found, add the new key-value to the linked end.
            if (!found) {
                buckets[index].add(pair);
            }
            return oldValue;
        }

        @Override
        public v get(Object key) {
            // Use hash code to get index.
            int index = key.hashCode() % SIZE;
            if (buckets[index] == null) {
                return null;
            }
            for (Entry<k, v> kvEntry : buckets[index]) {
                // If have this key, return value.
                if (kvEntry.getKey().equals(key)) {
                    return kvEntry.getValue();
                }
            }
            return null;
        }

        @Override
        public Set<Entry<k, v>> entrySet() {
            HashSet<Entry<k, v>> entryHashSet = new HashSet<>();
            for (LinkedList<Entry<k, v>> bucket : buckets) {
                if (bucket != null) {
                    entryHashSet.addAll(bucket);
                }
            }
            return entryHashSet;
        }
    }
  • 看了上面實現一個簡單的 HashMap 的原理後, 我們便明白為什麼當我們使用自定義的型別作為 鍵 的時候必須同時覆寫 equals 和hashcode 方法了.
  • 一個好的雜湊函式應該要能跟產生均勻的雜湊碼, 並且雜湊碼並不要求唯一, 更加關注的應該是其生成的速度,我們最好使用物件的某些有意義的資訊來生成雜湊碼.
  • 用來生成雜湊碼的資訊應該是不能輕易改變的值,如果你對同一個 key 呼叫 hashCode 卻生成了兩個不一樣的雜湊碼, 那麼你將無法獲取到之前的 value.

未完待續......對HashMap原始碼的解析.....

相關推薦

HashMap為什麼這麼? ---深入理解HashMap機制

在通過上篇部落格瞭解了 Map 的維持內部的 鍵-值 對的基本方式之後,----不瞭解的看這篇(Java集合的基本概括), 我們便會思考, 在 HashMap 內部是如何組織和排列這些封裝了 鍵-值對的 Map.Entry 實體呢? 如何組織才能達到更高的效率呢? 在瞭解

查詢--深入理解一致性演算法

注:本篇部落格只是講述了一致性雜湊的思想,我們會在之後講述分散式雜湊表以及一致性雜湊的一種實現(Chord演算法)。 什麼是一致性雜湊演算法? 引用自維基百科: 一致性雜湊是一種特殊的雜湊演算法。在使用一致雜湊演算法後,雜湊表槽位數(大小)的改變

深入理解hashmap(三)表和二叉搜尋樹的恩怨情仇

前面兩篇文章介紹了hashmap的原始碼和理論,今天把剩餘的部分紅黑樹講一下。理解好紅黑樹,對我們後續對hashmap或者其他資料結構的理解都是很有好處的。比方說為什麼後面jdk要把hashmap中的單鏈表更新成紅黑樹? 要理解紅黑樹首先要弄清楚普通二叉樹的一些基本概念 父節點和子節點,這個我就不多說了。

深入理解HashMap(及hash函數的真正巧妙之處)

ssa 什麽 關聯 表示 廣泛 要求 傳遞 所有 總結 原文地址:http://www.iteye.com/topic/539465 Hashmap是一種非常常用的、應用廣泛的數據類型,最近研究到相關的內容,就正好復習一下。網上關於hashmap的文章很多,但到底是自己

深入理解 HashMap

包含 刪除 不同 鍵值 code 1.8 信息 索引 導致 1. 簡介 HashMap 是Java開發中使用頻率最高的鍵值對數據類型容器。它根據鍵的哈希值(hashCode)來存儲數據,訪問速度高,但無法按照順序遍歷。HashMap 允許鍵值為空和記錄為空,非線程安全。 另

深入理解HashMap及面試相關問答

前言 HashMap是面試必備的一個知識點,無論你是初級中級還是高階,基本上逃不過這個問題,下面的內容很簡單,只要你理解了其中的含義,這對你使用hashmap和麵試都是很有幫助的。 正文 首先開啟HashMap,看看中都定義了哪些成員變數。 解釋幾個重點的變數 transi

深入理解hashmap理論篇

之前有過一篇介紹java中hashmap使用的,深入理解hashmap,比較側重於 程式碼分析,沒有從理論上分析hashmap,今天把hashmap的理論部分補充一下(之後應該還有兩篇補充 一篇講紅黑樹一篇講多執行緒)。 雜湊(雜湊)函式到底是幹嘛的?和雜湊表是啥關係?其主要作用和應用場景到底在哪裡? 簡

深入理解HashMap(原理,查詢,擴容)

面試的時候聞到了Hashmap的擴容機制,之前只看到了Hasmap的實現機制,補一下基礎知識,講的非常好 原文連結: Hashmap是一種非常常用的、應用廣泛的資料型別,最近研究到相關的內容,就正好複習一下。網上關於hashmap的文章很多,但到底是自己學習的總結,就

深入理解HashMap(一次性徹底掌握)

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

深入理解HashMap(及hash函式的真正巧妙之處)

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

從原始碼深入理解HashMap(附加HashMap面試題)

HashMap向來是面試中的熱點話題,深入理解了HashMap的底層實現後,才能更好的掌握它,也有利於在專案中更加靈活的使用。 本文基於JDK8進行解析 一、HashMap解析 1. 結構 HashMap結構由陣列加**連結串列(或紅黑樹)**構成。主幹是E

深入理解HashMap(精華必看)

3、hashmap的resize         當hashmap中的元素越來越多的時候,碰撞的機率也就越來越高(因為陣列的長度是固定的),所以為了提高查詢的效率,就要對hashmap的陣列進行擴容,陣列擴容這個操作也會出現在ArrayList中,所以這是一個通用的操作,很多人對它的效能表示過懷疑,不過想想我

深入理解HashMap及底層實現

概述:HashMap是我們常用的一種集合類,資料以鍵值對的形式儲存。我們可以在HashMap中儲存指定的key,value鍵值對;也可以根據key值從HashMap中取出相應的value值;也可以通過keySet方法返回key檢視進行迭代。以上是基於HashMap的常見應用,但是光會使用是遠

深入理解HashMap

什麼是HashMap HashMap作為Java語言中一種重要的型別,其儲存資料通過鍵值對的形式儲存,即<key,value>的形式。HashMap繼承AbstractMap類,最終實現的是Map介面。HashMap中資料的Key值不允許重複,Ha

深入理解HashMap原理

3、hashmap的resize        當hashmap中的元素越來越多的時候,碰撞的機率也就越來越高(因為陣列的長度是固定的),所以為了提高查詢的效率,就要對hashmap的陣列進行擴容,陣列擴容這個操作也會出現在ArrayList中,所以這是一個通用的操作,很多人對它的效能表示過懷疑,不過想想我們

深入理解HashMap原始碼

1.HashMap資料結構 首先先看下HashMap的資料結構圖 我們都知道陣列的儲存方式在是連續的,查詢速度比較快,但插入和刪除資料比較慢 而連結串列的儲存方式是非連續的,所以插入和刪除速度較快,但查詢速度就比較慢,HashMap在資料結構上兩種都採用了。

hashMap的原理 深入理解

首先再次強調hashcode (==)和equals的真正含義(我記得以前有人會說,equals是判斷物件內容,hashcode是判斷是否相等之類): equals:是否同一個物件例項。注意,是“例項”。比如String s = new String(“test”);

java的HashCode equals == 以及hashMap底層實現深入理解

1.==等號 對比物件例項的記憶體地址(也即物件例項的ID),來判斷是否是同一物件例項;又可以說是判斷物件例項是否物理相等;(參見:http://kakajw.iteye.com/blog/935226) 2.equals 檢視底層object的equals方法 publ

每天進步一點點——五分鐘理解一致性演算法(consistent hashing)

根據上面的圖解分析,一致性雜湊演算法滿足了單調性和負載均衡的特性以及一般hash演算法的分散性,但這還並不能當做其被廣泛應用的原由,因為還缺少了平衡性。下面將分析一致性雜湊演算法是如何滿足平衡性的。hash演算法是不保證平衡的,如上面只部署了NODE1和NODE3的情況(NODE2被刪除的圖),object1

[轉]理解一致性演算法(consistent hashing)

[size=medium]一致性雜湊演算法原理[url]http://www.cnblogs.com/lpfuture/p/5796398.html[/url]白話解析一致性雜湊演算法(Excellent)[url]http://www.zsythink.net/archiv