最近整理資料結構方面的知識點,hashmap是很重要的一部分,今天來聯合原始碼分析他的資料結構以及儲存方式!
接下來將從以下幾個方面來分析(根據jdk1.8)
1. 構造方法
2. 重要的幾個資料解釋
3. put
4. get
// 儲存資料的陣列 table
transient node table;
// 儲存資料的基本型別
static
class
node
implements
map.entry
getter setter
總共有四個構造方法
// initialcapacity:指定map的容量大小
// loadfactor: 指定載入因子, 預設是default_load_factor 0.75
public
hashmap(int initialcapacity, float loadfactor)
public
hashmap(int initialcapacity)
// 預設構造方法
public
hashmap()
// 包含「子map」的建構函式
public
hashmap(map extends k, ? extends v> m)
// 預設的載入因子
static
final
float default_load_factor = 0.75f;
首先來說明下hashmap的資料結構 (圖是從別的地方借用的)
在hashmap 進行儲存資料時,我覺得可以從三點來判斷 :
1 . 如何判定key的唯一性
2. 如何通過key計算hash值,並且可以通過hash值計算得到應該放置的陣列的位置
3. 什麼時候擴容
public v put(k key, v value)
final v putval(int hash, k key, v value, boolean onlyifabsent,
boolean evict)
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;} }
// 對於找到的相同的key的node,進行value覆蓋
v oldvalue = e.value;
if (!onlyifabsent || oldvalue == null)
e.value = value;
afternodeaccess(e);
return oldvalue;}}
// map 被修改的次數+1
++modcount;
// 如果長度達到臨界值,進行擴容
if (++size > threshold)
resize();
afternodeinsertion(evict);
return
null;
}static final int hash(object key)
通過上面的**注釋,應該對hashmap的儲存方式有更進一步的認識。整個過程就是我們拿到node的hash值,來計算應該放置的位置,不管value值是null或者和已經存在的值是否重複,只要找到位置後,放置進去,就ok。
final node resize() else
if ((newcap = oldcap << 1) < maximum_capacity &&
oldcap >= default_initial_capacity)
newthr = oldthr << 1; // double threshold
} else
if (oldthr > 0) // initial capacity was placed in threshold
newcap = oldthr;
else
if (newthr == 0)
threshold = newthr;
@suppresswarnings()
node newtab = (node) new node[newcap];
table = newtab;
if (oldtab != null) else
} while ((e = next) != null);
if (lotail != null)
if (hitail != null) }}
}}return newtab;
}
resize方法:無非就是重新申請空間,如果一開始是0,就採用和構造方法一樣的設定,預設初始大小16;如果之前有資料就成倍增加,並計算新的臨界值,重新通過hash值計算在陣列中的位置,把之前的資料拷貝到新申請的陣列上;陣列的擴容是非常耗費效能的,如果我們能提前預算出陣列的大小,我們在初始化時可以直接進行指定;
首先我們來看源**,讀取資料相對儲存資料簡單寫,我們只需要通過key,判斷key的合法性以及通過計算hash 定為元素在陣列的位置即可。
public v get(object key)
final nodegetnode(int hash, object key) while ((e = e.next) != null);}}
return
null;
}
經過上面的分析,我們知道hashmap主要通過陣列+鍊錶的形式儲存資料,當儲存的資料過多的時候,鍊錶越來越重,之後我們查詢起來時間也越來越長,效率越來越低,
我們可以看到其中(e instanceof treenode)
判斷節點是否是treenode,jdk1.8中,hashmap採用陣列+鍊錶+紅黑樹來實現,當鍊表長度超過閾值(8)時,將鍊錶轉換為紅黑樹,這樣大大減少了查詢時間。
static
final
class
treenode
extends
linkedhashmap.linkedhashmapentry
...}
和hashmap相似的hashset其實就是變形的hashmap,我們可參考這篇文章hashset簡單看下 他們的區別。
參考資料:
資料結構原始碼 迷宮
include include include include include define stack init size 1000 define stack more 10 define overflow 2 define ok 1 define error 0 define true 1 de...
java 資料結構 原始碼閱讀
collections工具類裡的 collections.synchronizedlist public static listsynchronizedlist listlist 僅僅是通過判斷是否實現randomaccess介面,而返回不同的synchronizedlist 內部 類,random...
資料結構課後題目原始碼
習題描述如下 假設以陣列q m 存放迴圈佇列中的元素,同時設定乙個標誌tag,以tag 0和tag 1來區別在隊頭指標 front 和隊尾指標 rear 相等時,佇列狀態為 空 還是為 滿 試編寫與此結構相應的插入 enqueue 和刪除 dequeue 演算法。以下是博主自己碼的 不喜勿噴!ifn...