1. 程式人生 > >HashMap原始碼分析-jdk1.6和jdk1.8的區別

HashMap原始碼分析-jdk1.6和jdk1.8的區別

在java集合中,HashMap是用來存放一組鍵值對的數,也就是key-value形式的資料,而在jdk1.6和jdk1.8的實現有所不同。

JDK1.6的原始碼實現:

首先來看一下HashMap的類的定義:

HashMap繼承了AbstractHashMap,實現了Map,Cloneable和Serializable介面,Map定義了一些公共的介面,而AbstractHashMap也實現了Map介面,並提供了一些預設的實現,如size方法和isEmpty方法等

在HashMap中,定義了幾個常量:

初始容量,如果在建立HashMap的時候沒有指定容量,就使用初始容量

最大容量,HashMap中儲存元素的陣列的最大的容量,為2的30次方

預設的載入因子,在擴容的時候使用

最重要的一個常量,用來存放我們新增的元素,

閾值,用來判斷HashMap是否需要擴容,如果新增的元素超過該值,則需要擴容, 該值等於 capacity * loadFactor,比如 預設的初始容量為16, 預設的載入因子為0.75,則閾值就等於16*0.75=12,在table陣列中,如果陣列的元素個數超過12,則table陣列就需要進行擴容。

HashMap提供了三個構造方法,我們可以指定初始容量和載入因子來構造HashMap

也可以只指定初始容量來構造HashMap

也可以都不指定,這時,初始容量和載入因子都是用的預設的值,一般情況下也不會去指定初始容量和載入因子。

如果採用不帶引數的構造方法,可以看到存放元素的初始陣列的大小為16,閾值為12。

相當於 Entry[] table = new Entry[16],在HashMap內部使用 Entry陣列來存放元素的。

可以看到Entry表示的是一個單向連結串列的結構,next就是指向下一個節點;也就是說在HashMap內部,使用陣列+連結串列的形式來存放元素,陣列的每一項就是一個連結串列。HashMap的結構圖大致如下所示:

接下來看一下對HashMap的常用操作:

1. put(key, value)操作,向HashMap中新增元素

1)新增的時候,首先要計算key的hash值,找到對應陣列的下標

2)找到該下標對應的陣列位置的連結串列,遍歷連結串列,把值新增到該連結串列上

addEntry()方法如下:

用圖來說明:

1. 初始化一個空的HashMap,此時還沒有元素,結構如下:

假設要新增一對資料:key="zs", value="zhangsan"

首先對 key進行hash,比如 hash之後的值為5,之後在用hash和table.length來求陣列的索引,

比如索引 i = 4,此時,這對元素就應該在 table[i] 即 table[4] 的位置處,取得該處的Entry連結串列,此時,連結串列為空,建立一個Entry節點,加入到該空連結串列中:

此時,在新增一對元素:key="ls", value="lisi",假如計算的索引 i 恰好等於4,此時,取得 table[4] 處的連結串列  Entry<K, V> = table[4], 用key = "ls"在這個連結串列上進行遍歷,看看是否該key已存在:

此時,key="ls"並不存在,又會建立一個Entry節點,加入到該列表中:

如果此時,又新增 key="zs", value="zhangsan222",根據key計算到的索引為4,取出 table[4]處的連結串列,遍歷連結串列,然後檢查對應的key是否存在,檢查到key已經存在了,所以會把新的值替換舊的值即可,不用建立新的節點。

2.get(key)操作

1)根據key計算hash值,根據hash值和陣列長度計算陣列的下標索引

2)取得該下標對應的連結串列,遍歷連結串列,找到key對應的value

3.remove()操作,

    1)根據key計算hash值,根據hash值和陣列長度計算陣列的下標索引

    2)取得該下標對應的連結串列,遍歷連結串列,刪除key對應的Entry節點

removeEntryForKey()方法如下:

假設現在HashMap中元素的分佈如下:

要刪除 key="ls"的元素,假如計算的索引 i=4,要去遍歷 table[4]處的連結串列,刪除對應的節點,key="ls"為連結串列的第一個節點:

以上就是jdk1.6中HashMap的實現,是基於陣列+連結串列的形式來存放資料的。

JDK1.8的原始碼實現:

在JDK1.8中,HashMap的實現比1.6的實現要複雜得多,1.8中引入了紅黑樹的資料結構;

除了上面列出來的常量外,新增加了幾個常量:

表示的是,如果陣列中連結串列的元素大於該值,則需要把該連結串列轉化為紅黑樹,

如果連結串列中的元素個數小於該值,則把紅黑樹轉換為連結串列

在JDK1.6中,使用一個Entry陣列來存放元素,而在JDK1.8中,使用的Node陣列和TreeNode來存放元素,

Node:其實,Node和Entry沒有什麼區別,

TreeNode:表示的是一個紅黑樹結構