1. 程式人生 > >HashMap原始碼分析(一):JDK原始碼分析系列

HashMap原始碼分析(一):JDK原始碼分析系列

正文開始 注:JDK版本為1.8

HashMap1.8和1.8之前的原始碼差別很大


  • 目錄
    • 簡介
      • 資料結構
    • 類結構
    • 屬性
    • 構造方法
    • 增加
    • 刪除
    • 修改
    • 總結

1.HashMap簡介

HashMap基於雜湊表的Map介面實現,是以key-value儲存形式存在。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)

HashMap 的實現不是同步的,這意味著它不是執行緒安全的。它的key、value都可以為null。此外,HashMap中的對映不是有序的。在 JDK1.8 中,HashMap 是由 陣列+連結串列+紅黑樹構成,新增了紅黑樹作為底層資料結構,結構變得複雜了,但是效率也變的更高效。

1.2 HashMap資料結構

在 JDK1.8 中,HashMap 是由 陣列+連結串列+紅黑樹構成,新增了紅黑樹作為底層資料結構,結構變得複雜了,但是效率也變的更高效。當一個值中要儲存到Map的時候會根據Key的值來計算出他的

hash,通過雜湊來確認到陣列的位置,如果發生雜湊碰撞就以連結串列的形式儲存在Object原始碼分析中解釋過,但是這樣如果連結串列過長來的話,HashMap會把這個連結串列轉換成紅黑樹來儲存。

來看依一下HashMap的儲存結構

但是這樣的話問題來了,HashMap為什麼要使用紅黑樹呢,這樣結構的話不是更麻煩了嗎??

這個問題我也沒有想過,其實很多在看的時候只會在乎紅黑樹的實現而忽略到了為什麼要使用的這個問題,我也是在寫本文的時候突發疑惑。參考了網上的例子,同時也解釋了為什麼閥值為8:

因為Map中桶的元素初始化是連結串列儲存的,其查詢效能是O(n),而樹結構能將查詢效能提升到O(log(n))。當連結串列長度很小的時候,即使遍歷,速度也非常快,但是當連結串列長度不斷變長,肯定會對查詢效能有一定的影響,所以才需要轉成樹。至於為什麼閾值是8,我想,去原始碼中找尋答案應該是最可靠的途徑。

參考地址:https://dwz.cn/nPFXmXwJ

2.類結構

我們來看一下類結構

在閱讀原始碼的時候一直有個問題很困惑就是HashMap已經繼承了AbstractMap而AbstractMap類實現了Map介面,那為什麼HashMap還要在實現Map介面呢?同樣在ArrayList中LinkedList中都是這種結構。

據 java 集合框架的創始人Josh Bloch描述,這樣的寫法是一個失誤。在java集合框架中,類似這樣的寫法很多,最開始寫java集合框架的時候,他認為這樣寫,在某些地方可能是有價值的,直到他意識到錯了。顯然的,JDK的維護者,後來不認為這個小小的失誤值得去修改,所以就這樣存在下來了。

  • Cloneable 空介面,表示可以克隆
  • Serializable 序列化
  • AbstractMap 提供Map實現介面

3.屬性

初始化容量(必須是二的n次冪)

集合最大容量(必須是二的冪)

負載因子,預設的0.75

當連結串列的值超過8則會轉紅黑樹(1.8新增)

當連結串列的值小於6則會從紅黑樹轉回連結串列

當Map裡面的數量超過這個值時,表中的桶才能進行樹形化 ,否則桶內元素太多時會擴容,而不是樹形化 為了避免進行擴容、樹形化選擇的衝突,這個值不能小於 4 * TREEIFY_THRESHOLD

table用來初始化(必須是二的n次冪)

用來存放快取

HashMap中儲存的數量

用來記錄HashMap的修改次數

用來調整大小下一個容量的值計算方式為(容量*負載因子)

雜湊表的載入因子

重點屬性

  • table 在JDK1.8中我們瞭解到HashMap是由陣列加連結串列加紅黑樹來組成的結構其中table就是HashMap中的陣列
  • Size 為HashMap中K-V的實時數量
  • loadFactor 載入因子,是用來衡量 HashMap 滿的程度,計算HashMap的實時載入因子的方法為:size/capacity,而不是佔用桶的數量去除以capacity。capacity 是桶的數量,也就是 table 的長度length。
  • threshold 計算公式:capacity * loadFactor。這個值是當前已佔用陣列長度的最大值。過這個數目就重新resize(擴容),擴容後的 HashMap 容量是之前容量的兩倍

4.構造方法

開始看構造方法。

4.1 HashMap()

構造一個空的 HashMap ,預設初始容量(16)和預設負載因子(0.75)。

4.2 HashMap(int initialCapacity)

構造一個空的 HashMap具有指定的初始容量和預設負載因子(0.75)。

4.3 HashMap(int initialCapacity, float loadFactor)

構造一個空的 HashMap具有指定的初始容量和負載因子。我們來分析一下。

最後呼叫了tableSizeFor,來看一下方法實現:

5.增加

現在我們開始分析put()方法

我們可以看到put呼叫的是putVal來進行資料插入,但是要注意到key在這裡執行了一下hash()方法,來看一下Hash方法是如何實現的。

從上面可以得知HashMap是支援Key為空的,而HashTable是直接用過Key來獲取HashCode所以key為空會拋異常其實上面就已經解釋了為什麼HashMap的長度為什麼要是2的冪因為HashMap 使用的方法很巧妙,它通過 hash & (table.length -1)來得到該物件的儲存位,前面說過 HashMap 底層陣列的長度總是2的n次方,這是HashMap在速度上的優化。當 length 總是2的n次方時,hash & (length-1)運算等價於對 length 取模,也就是 hash%length,但是&比%具有更高的效率。比如 n % 32 = n & (32 -1)。

現在看putVal()方法,看看它到底做了什麼。

主要引數:

  • hash key的hash值
  • key 原始Key
  • value 要存放的值
  • onlyIfAbsent 如果true代表不更改現有的值
  • evict 如果為false表示table為建立狀態

完整原始碼分析,放圖片的話會太長了,所以就截取了一下分為兩部。

暫時分析到新增 ,首發亂敲程式碼公眾號

相關推薦

HashMap原始碼分析():JDK原始碼分析系列

正文開始 注:JDK版本為1.8 HashMap1.8和1.8之前的原始碼差別很大 目錄 簡介 資料結構 類結構 屬性 構造方法 增加 刪除 修改 總結 1.HashMap簡介 HashMap基於雜湊表的Map介面實現,是以key-value儲存形式存在。(除了不同步和允許使用 null

LinkedList原始碼分析JDK原始碼分析系列

如果本文中有不正確的地方請指出由於沒有留言可以在公眾號新增我的好友共同討論。 1.介紹 LinkedList 是執行緒不安全的,允許元素為null的雙向連結串列。 2.繼承結構 我們來看一下LinkedList的繼承結構圖: 程式碼實現: public class LinkedList<E>

深入JDK原始碼_Index --> 深入JDK原始碼之ThreadLocal類 --> 陶邦仁 又發現牛人

深入JDK原始碼 http://my.oschina.net/xianggao/blog/392440 ThreadLocal概述 學習JDK中的類,首先看下JDK API對此類的描述,描述如下: 該類提供了執行緒區域性 (thread-local) 變數。

AQS原始碼解析()-AtomicBoolean原始碼解析

基本類: AtomicInteger AtomicLong AtomicBoolean 陣列型別: AtomicIntegerArray AtomicLongArray AtomicReferenceArray 介紹 由於在多執行緒條件下,如果對共享變數修改容易造成資料不一致的情況,所以對於共享變

JDK原始碼分析系列--HashMap(1.8)

HashMap的儲存結構 變數定義 /** * 預設的初始化容量,必須是2的n次冪 */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

HashMap原始碼分析()

之前有寫到ArrayList的原始碼分析,歡迎大家點開我的頭像檢視 對於HashMap這個類大家一定不陌生,想必多多少少用過或者瞭解過,今天我來和大家談談HashMap的原始碼,JDK為1.8 繼承AbstractMap類,實現map介面等等 當你不設定它的容量時預設的容量大小&n

jdk原始碼分析之collection,List,Set

前言 標題取得有點大,一口氣分析三塊的原始碼,看上去是個很大的話題,不過在個人看來,一方面,這三個都是介面,不涉及程式碼實現,讀起來比較快,另一方面,大家都知道List,Set這兩個介面都繼承自collection,他們之間存在關聯,所以放在一塊分析討論最能凸顯,這三塊介面

JDK原始碼分析(4)HashMap

JDK版本 HashMap簡介 HashMap基於雜湊表的 Map 介面的實現。此實現提供所有可選的對映操作,並允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證對映

JDK原始碼分析系列--ConcurrentHashMap(1.8)

定義變數 /** * node陣列最大容量 */ private static final int MAXIMUM_CAPACITY = 1 << 30; /** * 預設容量 */ pr

JDK原始碼分析(3)HashMap

JDK版本 HashMap簡介 HashMap基於雜湊表的 Map 介面的實現。此實現提供所有可選的對映操作,並允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證對映的順序,特別是它不保證該順序恆久不變。

圖解Java常用資料結構()\JDK原始碼分析(二)——LinkedList

最近在整理資料結構方面的知識, 系統化看了下Java中常用資料結構, 突發奇想用動畫來繪製資料流轉過程. 主要基於jdk8, 可能會有些特性與jdk7之前不相同, 例如LinkedList LinkedHashMap中的雙向列表不再是迴環的. HashMap中的單鏈表

JDK原始碼分析系列-ArrayList

1、ArrayList本質 陣列 + 動態擴容實現的資料列表。 private static final Object[] EMPTY_ELEMENTDATA = {}; // elementData初始為空陣列 public ArrayList() { super(); this.

JDK原始碼分析】淺談HashMap的原理

這篇文章給出了這樣的一道面試題: 在 HashMap 中存放的一系列鍵值對,其中鍵為某個我們自定義的型別。放入 HashMap 後,我們在外部把某一個 key 的屬性進行更改,然後我們再用這個 key 從 HashMap 裡取出元素,這時候 HashMap 會返回什麼? 文中已給出示例程式碼與答案, k

JDK原始碼分析之String()

摘要 日常使用java中,java.lang.String類幾乎是使用最為頻繁的一個類,此係列主要介紹String的具體實現以及作者對其進行了什麼樣的優化操作。 實現 public final class String implements jav

HashMap原始碼之hash()函式分析JDK 1.8)

    我們知道,使用雜湊的容器,其高效能的主要影響因素之一就是hash值。     在HashMap中,為了更好的效能,我們希望作為Key的物件提供一個合理的hash函式以便能將其合理的分配到桶中。     而在實際的HashMap中,對從物件獲取的hash值又

詳解Tomcat系列()-從原始碼分析Tomcat的啟動

在整個Tomcat系列文章講解之前, 我想說的是雖然整個Tomcat體系比較複雜, 但是Tomcat中的程式碼並不難讀, 只要認真花點功夫, 一定能啃下來. 由於篇幅的原因, 很難把Tomcat所有的知識點都放到同一篇文章中, 我將把Tomcat系列文章分為Tomcat的啟動, Tomcat中各模組的介紹和

JDK原始碼分析系列---String,StringBuilder,StringBuffer

1.String public final class String implements java.io.Serializable, Comparable<String>, CharSequence { //儲存字元,final修飾 private final char

JDK原始碼分析系列02---ArrayList和LinkList

ArrayList和LinkList的原始碼分析 概要 ArrayList和LinkList是常用的儲存結構,不看原始碼先分析字面意思,Array意思是陣列,可知其底層是用陣列實現的,Link意思是連結,可知是以連結串列實現,這兩種資料結構各有什麼特點呢?在實際開發中,我們要如何選擇? 1.ArrayL

HashSet原始碼分析:JDK原始碼系列

1.簡介 繼續分析原始碼,上一篇文章把HashMap的分析完畢。本文開始分析HashSet簡單的介紹一下。 HashSet是一個無重複元素集合,內部使用HashMap實現,所以HashMap的特徵耶繼承了下來。儲存的元素是無序的並且HashSet允許使用空的元素。 HashSet是非同步的。如果多個執行緒同時

JDK原始碼分析-HashMap

一.HashMap的內部屬性 1.1 成員變數 1.1.1 size: HashMap包含的KV鍵值對的數量,也就是我們通常呼叫Map.size()方法的返回值 public int size() { return size; } 1.1.2 modCount HashMa