之前看過一下其他的文章若债,有幾篇寫的比較好的充坑,記得是美團(tuán)的技術(shù)團(tuán)隊(duì)分享的,結(jié)合自己看源碼做的筆記, 之前都只是作為自己的筆記存起來的,現(xiàn)在分享出來泽本,可能有人會(huì)看,哈哈淘太,太自戀了,筆記里面有些是其他 文章的分析规丽,圖是我自己整理的蒲牧,要是有寫分析借鑒了你的文章沒注明出處,先道歉,因?yàn)楦舻奶昧硕妮海挥浀檬窃谀目吹奈恼吕脖馈:?jiǎn)書的富文本編輯不能像有道筆記那樣可以顯示顏色的,我就先截長(zhǎng)圖吧艘狭。
Jdk8版本的源碼改動(dòng)很多挎扰,還引入了紅黑樹的數(shù)據(jù)結(jié)構(gòu),具體幾個(gè)方法還是看源碼吧,因?yàn)槭枪P記,所以方法里面很多不重要的,比如拋異常的就沒記到筆記巢音,或者是用偽代碼代替遵倦。
int DEFAULT_INITIAL_CAPACITY = 16 //初始容量
int MAXIMUM_CAPACITY = 1 << 30; //最大容量
float DEFAULT_LOAD_FACTOR = 0.75f; //負(fù)載因子
int TREEIFY_THRESHOLD = 8; //需要用樹存儲(chǔ)時(shí)的臨界值
int UNTREEIFY_THRESHOLD = 6;
int MIN_TREEIFY_CAPACITY = 64; //樹的最低的容量
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
// h = key.hashCode() 為第一步 取hashCode值
// h ^ (h >>> 16) 為第二步 高位參與運(yùn)算
// h>>>16 是右移 16位, ^ 是異或運(yùn)算, 只有 0 ^ 1 或者 1^0 才是1,其他為 0
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i; //p是計(jì)算出來的下標(biāo)所在的結(jié)點(diǎn), i 是插入下標(biāo), n 是 table 長(zhǎng)度
if ((tab = table) == null || (n = tab.length) == 0) //table長(zhǎng)度為 32
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
//這里是取模運(yùn)算= hash % n
//key的哈希碼與 n-1 相與, 得到下標(biāo), n-1 可以減少哈希沖突概率,因?yàn)?^n-1變成2進(jìn)制會(huì)有很多個(gè) 1
// 如果是2^n變成二進(jìn)制會(huì)有很多個(gè) 0, 0跟什么相與都是0,1跟 跟 0港谊, 1相與會(huì)得到對(duì)應(yīng)的結(jié)果
tab[i] = newNode(hash, key, value, null); //如果該下標(biāo)結(jié)點(diǎn)為空, 直接插入結(jié)點(diǎn)
else {
Node<K,V> e; K k;
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
e = p; // 如果計(jì)算出來的數(shù)組下標(biāo)已經(jīng)有元素了, 直接用新的 value 替換該結(jié)點(diǎn)的 value
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { //查詢到鏈表的最后一個(gè)節(jié)點(diǎn)也沒有找到,那么新建一個(gè)Node,然后加到最后一個(gè)元素的后面
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) { //找到的待插入結(jié)點(diǎn) p 的后驅(qū)結(jié)點(diǎn)為空
p.next = newNode(hash, key, value, null); //將新的結(jié)點(diǎn)插入到 p.next, p是最后的結(jié)點(diǎn)
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
break; //如果在這個(gè)鏈表上找到與插入結(jié)點(diǎn)相等的結(jié)點(diǎn)骇吭,則不插入
p = e; //否則繼續(xù)在第一個(gè)結(jié)點(diǎn)的后驅(qū)結(jié)點(diǎn)繼續(xù)查找比較
}
}
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null) { e.value = value; }
afterNodeAccess(e);
// 如果計(jì)算出來的數(shù)組下標(biāo)已經(jīng)有元素了, 直接用新的 value 替換該結(jié)點(diǎn)的 value
//并返回舊的 value
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold; int newCap, newThr = 0;
if (oldCap > 0) { //將舊的容量翻倍, 舊的 臨界值翻倍
if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) {
newThr = oldThr << 1; // double threshold
}
}
else if (oldThr > 0) { newCap = oldThr; } //舊的 臨界值變?yōu)樾碌娜萘? else { // 設(shè)置默認(rèn)容量, 臨界值, 這里只出現(xiàn)在 第一次put就擴(kuò)容的時(shí)候
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
threshold = newThr;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab == null) {
return newTab;
}
// 這里的數(shù)組遷移比較消耗性能, 只需要把數(shù)組每個(gè)下標(biāo)中有元素的結(jié)點(diǎn)直接放到新數(shù)組下標(biāo)中,結(jié)點(diǎn)的引用鏈表還是沒變
for (int j = 0; j < oldCap; ++j) { //將舊的哈希表復(fù)制到新的哈希表,oldCap是數(shù)組容量
if ( ( (Node<K,V>) e = oldTab[j] ) == null ) {
continue;
}
oldTab[j] = null;
if (e.next == null) //舊的哈希表對(duì)應(yīng)的下標(biāo)只有一個(gè)結(jié)點(diǎn)時(shí),用哈希值重新計(jì)算新容量的下標(biāo)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead, loTail; hiHead, hiTail ; //all = null
Node<K,V> next;
do {
next = e.next;
// n=16 二進(jìn)制為 0 0 0 1 0 0 0 0
// n=16 n-1=15 二進(jìn)制為 0 0 0 0 1 1 1 1
// n=32 n-1=31 二進(jìn)制為 0 0 0 1 1 1 1 1
//原來的下標(biāo)為 5 二進(jìn)制為 0 0 0 0 0 1 0 1
// 5&16=0,說明下標(biāo)為5 的下劃線高位是0,與新容量31相與還是5
// 所以只需要知道原下標(biāo)的高位如果是0則使用原來的下標(biāo)歧寺,是1則原下標(biāo)+oldCap
if ((e.hash & oldCap) == 0) { //判斷原下標(biāo)的高位是否為0
if (loTail == null) {
loHead = e;
}
else {
loTail.next = e;
}
loTail = e;
}
else {
if (hiTail == null) {
hiHead = e;
}
else {
hiTail.next = e;
}
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) { //原下標(biāo)高位是 0 則使用原下標(biāo)
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
return newTab;
}
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do { //將普通的鏈表結(jié)點(diǎn) Node 轉(zhuǎn)化為 樹結(jié)點(diǎn), 保存 hash, key, value
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else
p.prev = tl;
tl.next = p;
tl = p;
} while ((e = e.next) != null);
//上面的循環(huán)目的是將 下標(biāo)對(duì)應(yīng)的鏈表的 Node 全部替換為 樹結(jié)點(diǎn) TreeNode
if ((tab[index] = hd) != null){
hd.treeify(tab); //將構(gòu)造好的 TreeNode 鏈表轉(zhuǎn)為紅黑樹, 提升查詢時(shí)間復(fù)雜度
}
}
}
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))){
//先根據(jù)key算到 hash值燥狰,算出下標(biāo)棘脐,如果下標(biāo)的第一個(gè)結(jié)點(diǎn)的 hash值相等
//并且key相等就直接返回第一個(gè) 結(jié)點(diǎn)
return first;
}
if ((e = first.next) != null) {
if (first instanceof TreeNode) {
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
}
do { //否則逐個(gè)對(duì)比下標(biāo)上的鏈表結(jié)點(diǎn),相等條件為 hash, key相等
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
return e;
}
} while ((e = e.next) != null);
}
}
return null;
}