HashMap源碼中的成員變量你還不懂? 來來來!!!整理好的成員變量源碼解析

前言

點贊在看,養成習慣。java

點贊收藏,人生輝煌。web

點擊關注【微信搜索公衆號:編程背鍋俠】,防止迷路。編程

HashMap集合簡介

概述

HashMap基於哈希表的Map接⼝口實現,是以key-value存儲形式存在,即主要⽤用來存放鍵值對。它的key、value均可覺得null。 HashMap 的實現不是同步的,這意味着它不是線程安全的。此外, HashMap中的映射不是有序的,位置由hashcode通過運算決定。數組

數據結構
  • [ ] 在JDK1.8 以前 HashMap 由 數組+ 鏈表 數據結構組成的。
  • [x] 在JDK1.8 以後 HashMap 由 數組+鏈表 +紅⿊樹數據結構組成的。
數據結構解析

JDK1.8 以前 HashMap 由數組+鏈表組成的,數組是 HashMap 的主體,鏈表則是主要爲了了解決哈希衝突**(兩個對象調用的hashCode⽅法計算的哈希碼值一致而致使計算的數組索引值相同)而存在的(「拉鍊法」解決衝突).JDK1.8 之後在解決哈希衝突時有了較大的變化,當鏈表長度大於閾值(或者紅黑樹的邊界值,默認爲 8)而且當前數組的長度大於64**時【同時知足這兩個條件】,此時此索引位置上的全部數據改成使用紅⿊樹存儲。緩存

補充:將鏈表轉換成紅黑樹前會判斷,即便閾值大於8,可是數組長度小於64,此時並不會將鏈表變爲 紅黑樹。⽽是選擇進行數組擴容。安全

爲何要知足以上兩個條件?

這樣作的目的是由於數組比較小的時候要盡量避開紅黑樹結構,這種狀況下變爲紅黑樹結構,反而會下降效率,由於紅⿊樹須要進行左旋,右旋,變色這些操做來保持平衡 。同時數組⻓度⼩於64時,搜索時間相對要快些。因此綜上所述爲了提升性能和減小搜索時間,底層在閾值大於8而且數組長度大於64時, 鏈表才轉換爲紅黑樹。具體能夠參考 treeifyBin 方法。微信

固然雖然增了了紅黑樹做爲底層數據結構,結構變得複雜了,可是閾值大於8而且數組長度大於64時,鏈表轉換爲紅黑樹時,效率也變的更⾼效。數據結構

特色分析
  • 存取是無序的。
  • 鍵和值都均可以是null,可是這些鍵中僅只能有一個是null。
  • 鍵位置是惟一的,底層的數據結構控制鍵的位置。
  • jdk1.8以前數據結構是:鏈表 + 數組, jdk1.8以後是 : 鏈表 + 數組 + 紅黑樹。
  • 閾值(邊界值) > 8 而且數組長度大於64,纔將鏈表轉換爲紅黑樹,變爲紅黑樹的⽬的是爲了高效的查詢。

HashMap繼承關係

HashMap繼承關係源碼
public class HashMap<K,V> extends AbstractMap<K,V>  implements Map<K,V>, Cloneable, Serializable {  // 省略 } 複製代碼
HashMap的繼承圖解

繼承圖解析
  • [x] Cloneable是空接口,表示能夠克隆隆。 表明建立並返回HashMap對象的一個副本。
  • [x] Serializable 序列化接口。屬於標記性接口。HashMap對象能夠被序列化和反序列化。
  • [x] AbstractMap 父類提供了Map實現接口。以最大限度地減小實現此接口所需的⼯做。
奇怪問題

補充:經過上述繼承關係咱們發現一個很奇怪的現象, 就是HashMap已經繼承了AbstractMap而 AbstractMap類實現了Map接⼝口,那爲什什麼HashMap還要在實現Map接⼝口呢?一樣在ArrayList中 LinkedList中都是這種結構。框架

小小插曲

據 Java集合框架的創始⼈人Josh Bloch描述,這樣的寫法是⼀個失誤。在java集合框架中,相似這樣的寫法不少,最開始寫Java集合框架的時候,他認爲這樣寫,在某些地方多是有價值的,直到他意識到搞錯了。顯然的,JDK的維護者,後來不認爲這個⼩小的失誤值得去修改,因此就這樣存在下來了。編輯器

HashMap成員變量

序列化版本號
private static final long serialVersionUID = 362498820763181265L;
複製代碼
集合的初始化容量【16】( 必須是二的n次冪 )
// 默認的初始容量是16 -- 1<<4至關於1*2的4次方---1*16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 複製代碼

1 << 4解析,就是講1對應的二進制左移4位。0000 0001【1】左移4位爲0001 0000【16】。

集合的最大容量【2的30次方】
// 集合最大容量的上限是:2的30次冪
static final int MAXIMUM_CAPACITY = 1 << 30; 複製代碼
默認的負載因子,默認值是0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
複製代碼
當鏈表的值超過8則會轉紅黑樹(JDK1.8新增)
static final int TREEIFY_THRESHOLD = 8;
複製代碼
當鏈表的值小於6則會從紅黑樹轉回鏈表
// 當桶(bucket)上的結點數小於這個值時樹轉鏈表
static final int UNTREEIFY_THRESHOLD = 6; 複製代碼
轉化爲紅黑樹對應的數組長度最小值
// 桶中結構轉化爲紅黑樹對應的數組長度最小的值
static final int MIN_TREEIFY_CAPACITY = 64; 複製代碼

當Map⾥面的數量超過這個值時,表中的桶才能進行樹形化 ,不然桶內元素太多時會擴容,⽽不是樹形化。目的是爲了不進⾏擴容、樹形化選擇的衝突,這個值不能⼩於 4 * TREEIFY_THRESHOLD (8)

table⽤來初始化**(必須是二的n次冪)[重點]**
// 存儲元素的數組
transient Node<K,V>[] table; 複製代碼

table在JDK1.8中咱們了解到HashMap是由數組加鏈表加紅⿊樹來組成的結構其中table就是HashMap 中的數組,jdk8以前數組類型是Entry<K,V>類型。從jdk1.8以後是Node<K,V>類型。只是換了個名字, 都實現了同樣的接口:Map.Entry<K,V>。負責存儲鍵值對數據的。

⽤來存放緩存
// 存放具體元素的集合
transient Set<Map.Entry<K,V>> entrySet; 複製代碼

java語言的關鍵字,變量修飾符,若是用transient聲明一個實例變量,當對象存儲時,它的值不須要維持。換句話說,用transient關鍵字標記的成員變量不參與序列化過程。

當一個對象被序列化的時候,transient型變量的值不包括在序列化的表示中,然而非transient型的變量是被包括進去的。

HashMap中存放元素的個數**[重點]**
// 存放元素的個數,注意這個不等於數組的長度。
transient int size; 複製代碼

size爲HashMap中K-V的實時數量,不是數組table的⻓度。

⽤來記錄HashMap的修改次數
// 每次擴容和更改map結構的計數器
transient int modCount; 複製代碼
⽤來調整大小下一個容量的值計算方式爲(容量*負載因子)
// 臨界值 當實際⼤小(容量*負載因⼦)超過臨界值時,會進行擴容
int threshold; 複製代碼
哈希表的加載因子**[重點]**
// 加載因子
final float loadFactor; 複製代碼

loadFactor加載因子,是⽤來衡量 HashMap 中元素滿的程度,表示HashMap的疏密程度,能夠影響hash操做到同一個數組位置的機率,計算HashMap的實時加載因子的方法爲:size/capacity,⽽不是佔用桶的數量去除以capacity。capacity 是桶的數量,也就是 table 的⻓度length。size是集合中實際存儲元素的個數。

loadFactor太⼤致使查找元素效率低,過小致使數組的利用率低,存放的數據會很分散。loadFactor 的默認值爲0.75f是官方給出的⼀個比較好的臨界值。

HashMap⾥面容納的元素已經達到HashMap數組⻓度的75%時,表示HashMap太擠了,須要擴容,⽽而擴容這個過程涉及到 rehash、複製數據等操做,很是消耗性能。因此開發中盡量減小擴容的次數,能夠經過建立HashMap集合對象時指定初始容量來盡量避免。

同時在HashMap的構造器中能夠定製loadFactor。這個不經常使用。這個構造方法會在接下來的文章中講解。

  • 創做不易, 很是歡迎你們的點贊、評論和關注(^_−)☆
  • 你的點贊、評論以及關注是對我最大的支持和鼓勵,而你的支持和鼓勵
  • 我繼續創做高質量博客的動力 !!!
相關文章
相關標籤/搜索