1. 程式人生 > >深入理解HashMap原始碼

深入理解HashMap原始碼

1.HashMap資料結構

首先先看下HashMap的資料結構圖

我們都知道陣列的儲存方式在是連續的,查詢速度比較快,但插入和刪除資料比較慢

而連結串列的儲存方式是非連續的,所以插入和刪除速度較快,但查詢速度就比較慢,HashMap在資料結構上兩種都採用了。

    /**
     * The table, initialized on first use, and resized as
     * necessary. When allocated, length is always a power of two.
     * (We also tolerate length zero in some operations to allow
     * bootstrapping mechanics that are currently not needed.)
     */
    transient Node<K,V>[] table;

再看下Node中的變數 (Node中含有hash值、key、value以及指向下個節點的next)

        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

通過這個table變數我們就能理解上面那個資料結構圖,它不僅使用了陣列,而且還採用了連結串列結構。

2.HashMap變數

首先我們看下HashMap中的變數,在看之前思考幾個問題:

1. HashMap資料結構既然使用了陣列,那麼陣列預設記憶體大小是多少?

2.當已使用記憶體大小超過總記憶體的一個百分比後,需要繼續增大記憶體,這個百分比是多少?

3.當連結串列節點長多超過多少時儲存結構變成紅黑樹?

    /**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

由此可以知道陣列的預設大小是16

    /**
     * The maximum capacity, used if a higher value is implicitly specified
     * by either of the constructors with arguments.
     * MUST be a power of two <= 1<<30.
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

當陣列不夠大的時候,就會增加記憶體大小,大小的上限為1<<30,也就是2的30次方

    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

這個變數就是問題2的答案,也被稱為負載因子,預設為0.75

    /**
     * The bin count threshold for using a tree rather than list for a
     * bin.  Bins are converted to trees when adding an element to a
     * bin with at least this many nodes. The value must be greater
     * than 2 and should be at least 8 to mesh with assumptions in
     * tree removal about conversion back to plain bins upon
     * shrinkage.
     */
    static final int TREEIFY_THRESHOLD = 8;

這個變數就是問題三的答案,當節點數量超過8時,儲存結構轉換成紅黑樹

  /**
     * The bin count threshold for untreeifying a (split) bin during a
     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
     * most 6 to mesh with shrinkage detection under removal.
     */
    static final int UNTREEIFY_THRESHOLD = 6;

當bin的個數小於6時由樹形結構調整為列表

    /**
     * The number of key-value mappings contained in this map.
     */
    transient int size;

當前HashMap中儲存key-value的大小

    /**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient int modCount;

HashMap被修改和刪除的次數

3.HashMap的儲存過程

        HashMap hashMap = new HashMap();
        hashMap.put("1","1230");
        System.out.print(hashMap.get("1"));

我們從hashMap.put()方法開始

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

這裡詳細講下hash()函式,首先先看原始碼:

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

如果key的值不為空的話,就把變數h等於key值的hashCode,然後把h的高16位與低16位進行一個異或運算,這樣得到的值和陣列的預設長度16相對應,方便後續計算該Node儲存的陣列下標是多少。

然後回到putVal()方法

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)//初始化tab
            n = (tab = resize()).length;//n為陣列總長度
        if ((p = tab[i = (n - 1) & hash]) == null)//i為儲存的陣列下標
//傳入的hash值與陣列長度進行位與運算,這樣就可以保證數值不越界,得到結果賦值給i,
//如果i處為空的話,就把node儲存到該下標
            tab[i] = newNode(hash, key, value, null);
        else {//如果下標i處存有值的話,就使用連結串列結構,此時p=tab[i]
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;//key值存在的話,就不存
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

未完。。。。待續

相關推薦

深入理解HashMap原始碼

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

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

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

深入理解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的理論部分補充一下(之後應該還有兩篇補充 一篇講紅黑樹一篇講多執行緒)。 雜湊(雜湊)函式到底是幹嘛的?和雜湊表是啥關係?其主要作用和應用場景到底在哪裡? 簡

手把手教你深入理解Spring原始碼-spring開篇(中)

授人以魚不如授人以漁,《手把手教你深入理解Spring原始碼》專欄教你如何學習、思考、閱讀Spring框架,並應對其它開源框架不再畏懼。 接著上篇的文章講,上篇的文章講述了什麼是IOC,這篇講述什麼又是AOP? 一樣的在看這篇文章之前,大家不妨先花點時間思考一下。 1、AOP的設計原理

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

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

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

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

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

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

深入HashMap 原始碼

這裡的 jdk 環境是 1.8 會和 1.7 的有區別 一、static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 預設初始化容量,值為 1

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

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

深入理解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中,所以這是一個通用的操作,很多人對它的效能表示過懷疑,不過想想我們

深入理解React原始碼

介面更新本質上就是資料的變化。通過把所有會動的東西收斂到狀態(state),React提供了一個非常直觀的前端框架。我也比較喜歡review基於React程式碼,因為我一般都是從資料結構開始看,這樣可以在鑽到細節程式碼之前建立對整個邏輯的初步理解。我也經常會好奇React

深入理解Spring原始碼之自動裝配

自動裝配;          Spring利用依賴注入(DI),完成對IOC容器中中各個元件的依賴關係賦值;    1)、@Autowired:自動注入:          1)、預設優先按照型別去容器中找對應的元件:applicationContext.getBean(Bo

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

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

深入理解OkHttp原始碼(一)——提交請求

本篇文章主要介紹OkHttp執行同步和非同步請求的大體流程。主要流程如下圖: 主要分析到getResponseWidthInterceptorChain方法,該方法為具體的根據請求獲取響應部分,留著後面的部落格再介紹。 Dispatcher類