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:表示的是一個紅黑樹結構

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章