Java類集框架 HashMap原始碼分析

2021-09-11 12:39:16 字數 4725 閱讀 5575

hashmap是基於map的鍵值對對映表,底層是通過陣列、鍊錶、紅黑樹(jdk1.8加入)來實現的。

hashmap結構

hashmap中儲存元素,是將keyvalue封裝成了乙個node,先以乙個node陣列的來儲存,通過keyhashcode來計算hash值,根據hash值和hashmap的大小確定存入元素在陣列中的位置。當hashcode相同時,即產生了相同的陣列索引位置,那麼就會通過單向鍊錶的形式來繼續儲存。

static class nodeimplements map.entry

// 省略部分**...

}複製**

hashmap中所有的對映都儲存在節點node中,同時為了解決發生hash碰撞的衝突,節點可以持有下乙個節點的引用,以形成乙個單向鍊錶。

hashmap結構圖(jdk1.7及之前)

在jdk1.8,hashmap又做了一些改動,當陣列table某個索引位置的上鍊表的長度大於8的話,則會將鍊錶轉化為紅黑樹。

static final class treenodeextends linkedhashmap.entry

// 省略部分**...

}複製**

同樣地,對映的key-value就儲存在treenode中。parentleftright持有相應節點的引用形成紅黑樹。

hashmap結構圖(jdk1.8)

hashmap原始碼分析

主要屬性:

transient node table; // 陣列

transient int size; // 大小

int threshold // 擴容閾值

final float loadfactor; // 載入因子,預設值為0.75複製**

構造方法:

// 使用預設的初始容量和載入因子

public hashmap

() // 指定初始容量,使用預設的載入因子

public hashmap(int initialcapacity)

// 用現有的map來構造乙個新的hashmap

public hashmap(map<? extends k, ? extends v> m)

// 根據自定義的初始容量和載入因子來構造hashmap

public hashmap(int initialcapacity, float loadfactor) else

if (initialcapacity < default_initial_capacity)

if (loadfactor <= 0 || float.isnan(loadfactor))

throw new illegalargumentexception("illegal load factor: " +

loadfactor);

threshold = initialcapacity;

init();

}複製**

建構函式主要是設定hashmap的初始容量,以及擴容的載入因子。hashmap(map<? extends k, ? extends v> m)建構函式根據已有的對映來構造新的hashmap,它同樣採用的預設的載入因子,並將m中的元素新增到新構造的hashmap中。

資料存放:

public void putall(map<? extends k, ? extends v> m) 

final void putmapentries(map<? extends k, ? extends v> m, boolean evict)

else

if (s > threshold)

resize();

for (map.entry<? extends k, ? extends v> e : m.entryset())

}}複製**

putall方法直接呼叫putmapentriesputmapentries方法中先根據已有的map中的元素數量對新構造的hashmap進行擴容,然後遍歷舊的map,取出元素存放到新的hashmap中。

// 存放key-value

public v put(k key, v value)

// 根據key的hashcode來計算hash值

static final int hash(object key)

final v putval(int hash, k key, v value, boolean onlyifabsent,

boolean evict)

// 如果鍊錶中存在與新加入的元素key相同,則直接替換掉

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k))))

break;

p = e;}}

v oldvalue = e.value;

if (!onlyifabsent || oldvalue == null)

e.value = value;

afternodeaccess(e);

return oldvalue;}}

++modcount;

// 新增完成後,檢查是否需要擴容

if (++size > threshold)

resize();

afternodeinsertion(evict);

return null;

}複製**

put方法的主要邏輯:根據新增節點的hash值計算計算它在陣列中的位置i,判斷tab[i]是否為空,為空則直接加入;不為空的話,需要判斷該節點的key是否與新加入的節點的key相同,相同的話直接替換;如果不同則需要判斷tab[i]節點是否是紅黑樹節點,如果是紅黑樹節點,則直接加入到紅黑樹中;如果不是紅黑樹節點,那肯定就是鍊錶的第乙個節點了,遍歷鍊錶,在遍歷的過程中還需要判斷是否與鍊錶中已有節點的key相同,如果相同,同樣直接替換掉,都不同的話就直接新增到鍊錶的末尾。並且呢,加入鍊錶後還需要判斷鍊錶的長度是否超過了閾值8,超過了的話,需要將鍊錶轉換為紅黑樹。

hashmap在新增資料的時候,會判斷當前資料量是否超過設定的閾值,如果超過的話會進行擴容,在擴容過程中會將已新增的資料進行重新新增,以致原來新增元素的順序和位置都改變了,所以hashmap不能保證元素的存入取出順序。

刪除資料:

// 根據key刪除資料

public v remove(object key)

// 根據key-value刪除資料

@override

public boolean remove(object key, object value)

// 刪除節點

final noderemovenode(int hash, object key, object value,

boolean matchvalue, boolean movable)

p = e;

} while ((e = e.next) != null);}}

// 匹配的刪除節點node不為null的話,刪除node

if (node != null && (!matchvalue || (v = node.value) == value ||

(value != null && value.equals(v))))

}return null;

}複製**

remove的邏輯和加入元素的邏輯相似,依次從陣列、紅黑樹、鍊錶中找到匹配的刪除節點來刪除。

clear方法:

public void clear

() }複製**

clear方法要簡單些,直接遍歷陣列tab,將陣列中所有元素都置空即可。

最後對於hashmap,我們只要知道了它的底層結構,要理解它的實現原理還是非常簡單。在jdk1.8之後,加入了紅黑樹的結構,使hashmap的效率比之前的版本又優化了很多,關於鍊錶轉化為紅黑樹,以及紅黑樹轉鍊錶的具體實現等細節後續再做分析。

Java類集框架 HashMap原始碼分析

hashmap是基於map的鍵值對對映表,底層是通過陣列 鍊錶 紅黑樹 jdk1.8加入 來實現的。hashmap結構 hashmap中儲存元素,是將key和value封裝成了乙個node,先以乙個node陣列的來儲存,通過key的hashcode來計算hash值,根據hash值和hashmap的大...

Java 類集框架

主要方法 add 增加資料 clear 清空資料 contains 是否包含某個資料 isempty 是否為空 remove 移除某個資料 size 獲取集合中的資料個數 toarray 轉換為物件陣列 iterator 例項化父介面iterator 物件陣列使用remove 和contains 時...

java基礎 類集框架二

學習筆記 set hashset treeset 一 set簡介 set 元素是無序 存入和取出的順序不一定一致 元素不可以重複 hashset 底層資料結構是雜湊表 hashset是如何保證元素唯一性的呢?如果元素的hashcode值相同,才會判斷equals是否為true 如果元素的hashco...