【Java X 原始碼剖析】Map的原始碼分析--JDK1.8-仍在更新
Map介面下的結構
目錄
④public boolean containsKey(Object Key)
public boolean remove(Object key, Object value)
LinkedHashMap(雜湊表+非迴圈雙向連結串列)[1.8不是迴圈]
① public V put(K key, V value)
② final int compare(Object k1, Object k2)
③private void fixAfterInsertion(Entry x),v>
HashTable(陣列+連結串列)[Synchronized實現執行緒安全-鎖住整個table]
①public synchronized V put(K key, V value)
HashMap(雜湊表+連結串列or紅黑樹)
通過Node陣列進行儲存,雜湊衝突就通過連結串列或者紅黑樹進行儲存
Node<K,V>實現了父類介面Map.Entry<K,V>
**陣列下標index的求值:
①public V Put(K key, V value)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
//hashCode()是本地方法
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;
//如果儲存元素的table為空,進行必要欄位的初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//如果根據hash值取得的結點為null,就新建一個結點p,否則返回結點p(注意p在tab的位置)
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//如果插入的結點hash和key都相同
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//雜湊衝突:如果是紅黑樹結點,就進行紅黑樹插入
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//雜湊衝突:連結串列形式
else {
for (int binCount = 0; ; ++binCount) {
//已經遍歷到最後一個結點p了,e為p.next,也就是鏈尾插入e
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//連結串列長度大於8,將連結串列轉換成紅黑樹
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//這時e已經等於p.next了,如果連結串列中有相同的key,就退出
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
//繼續遍歷下一個連結串列結點
p = e;
}
}
//如果不為null,此時e指向key相同hash相同的結點
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
//覆蓋舊值
e.value = value;
//子類LinkedHashMap中重寫,HashMap中是空函式
afterNodeAccess(e);
//返回舊值
return oldValue;
}
}
//更改操作次數
++modCount;
//大於儲存空間臨界值,就擴容,並將原先陣列元素放到新陣列中
//因為有連結串列、紅黑樹之類,所以還要調整他們
if (++size > threshold)
resize();
//子類LinkedHashMap中重寫,HashMap中是空函式
afterNodeInsertion(evict);
return null;
}
借鑑一張圖,侵權即刪
②final void treeifyBin(Node<K,V>[] tab, int hash)
//樹化前預備操作
//插入一個元素之後,判斷hash對應桶結點數>=8就執行此函式進行連結串列樹化
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 {
//此方法返回一個有e資料(hash/key/value)的樹節點p,null傳入的是e.next
TreeNode<K,V> p = replacementTreeNode(e, null);
//遍歷的第一層:相當於設定根結點hd
if (tl == null)
hd = p;
else {
//和原連結串列順序一致:雙向連結串列
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
//進行樹化
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
// For treeifyBin
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
return new TreeNode<>(p.hash, p.key, p.value, next);
}
③final void treeify(Node<K,V>[] tab)
/**
* Forms tree of the nodes linked from this node.
* @return root of tree
*/
//此時連結串列已經是雙向連結串列了
final void treeify(Node<K,V>[] tab) {
TreeNode<K,V> root = null;
//呼叫此方法時用的是頭結點Node.treeify(),所以this代表需要樹化雙向連結串列頭結點
for (TreeNode<K,V> x = this, next; x != null; x = next) {
next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
//迴圈第一層:設定頭結點
if (root == null) {
x.parent = null;
x.red = false;
root = x;
}
else {
K k = x.key;
int h = x.hash;
Class<?> kc = null;
//p是從根結點用來搜尋x應該插入到哪的指標
for (TreeNode<K,V> p = root;;) {
int dir, ph;
K pk = p.key;
//*注意:hash不等同於陣列下標,所以hash是可以不同的,陣列下標是一定相同的
//dir == -1 代表往p的右走
if ((ph = p.hash) > h)
dir = -1;
//dir == 1 代表往p的左走
else if (ph < h)
dir = 1;
//走到這裡代表x的hash和p的hash相同,就比較key
else if ((kc == null &&
//如果key沒有實現comparable介面或者 x和p的key也是相同的
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
//定義的規則(比較判空+類名+hashCode())進行判斷走左還是走右
dir = tieBreakOrder(k, pk);
//記錄父節點xp
TreeNode<K,V> xp = p;
//代表x就插入在xp的下層
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
//紅黑樹插入平衡(通過左右旋和改變結點顏色保證符合紅黑樹要求)
root = balanceInsertion(root, x);
break;
}
}
}
}
moveRootToFront(tab, root);
}
static int tieBreakOrder(Object a, Object b) {
int d;
if (a == null || b == null ||
//字典序法比較兩個類名是否一致,如果返回0代表類相同
(d = a.getClass().getName().
compareTo(b.getClass().getName())) == 0)
//通過hashcode()大小來比較
d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
-1 : 1);
return d;
}
④static <K,V> TreeNode<K,V> balanceInsertion
(TreeNode<K,V> root, TreeNode<K,V> x)
//每次插入一個紅黑樹結點就呼叫此方法進行一次調整,使其滿足紅黑樹要求
//root是紅黑樹根結點,x是新插入的結點。最後返回撥整後紅黑樹根結點
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
//新節點設定成紅色
x.red = true;
//xp是x的父節點/xpp是xp的父節點/xppl是xpp左結點/xppr是xpp右結點
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
//紅黑樹只有一個結點x,顏色設定成黑色(紅黑樹根節點是黑色)
if ((xp = x.parent) == null) {
x.red = false;
return x;
}
//只有兩層(xp->x)
else if (!xp.red || (xpp = xp.parent) == null)
return root;
//如果x父節點是x爺結點的左節點
if (xp == (xppl = xpp.left)) {
if ((xppr = xpp.right) != null && xppr.red) {
xppr.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
if (x == xp.right) {
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateRight(root, xpp);
}
}
}
}
else {
if (xppl != null && xppl.red) {
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
if (x == xp.left) {
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateLeft(root, xpp);
}
}
}
}
}
}
⑤static <K,V> void moveRootToFront
(Node<K,V>[] tab, TreeNode<K,V> root)
/**
* Ensures that the given root is the first node of its bin.
*/
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
int n;
if (root != null && tab != null && (n = tab.length) > 0) {
int index = (n - 1) & root.hash;
TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
if (root != first) {
Node<K,V> rn;
tab[index] = root;
TreeNode<K,V> rp = root.prev;
if ((rn = root.next) != null)
((TreeNode<K,V>)rn).prev = rp;
if (rp != null)
rp.next = rn;
if (first != null)
first.prev = root;
root.next = first;
root.prev = null;
}
assert checkInvariants(root);
}
}
③final TreeNode<K,V> putTreeVal
(HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v)
/**
* Tree version of putVal.
*/
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
int h, K k, V v) {
Class<?> kc = null;
boolean searched = false;
TreeNode<K,V> root = (parent != null) ? root() : this;
for (TreeNode<K,V> p = root;;) {
int dir, ph; K pk;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
if (!searched) {
TreeNode<K,V> q, ch;
searched = true;
if (((ch = p.left) != null &&
(q = ch.find(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.find(h, k, kc)) != null))
return q;
}
dir = tieBreakOrder(k, pk);
}
TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
Node<K,V> xpn = xp.next;
TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
if (dir <= 0)
xp.left = x;
else
xp.right = x;
xp.next = x;
x.parent = x.prev = xp;
if (xpn != null)
((TreeNode<K,V>)xpn).prev = x;
moveRootToFront(tab, balanceInsertion(root, x));
return null;
}
}
}
②resize()
//初始化(當前table==null)或者擴容之後元素調整(++size > threshold --> resize())
final Node<K,V>[] resize() {
//oldTab是舊元素陣列
Node<K,V>[] oldTab = table;
//舊陣列長度
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//舊陣列擴容臨界值
int oldThr = threshold;
//新陣列長度以及擴容值
int newCap, newThr = 0;
//如果舊陣列不為空
if (oldCap > 0) {
//陣列的長度已經達到最大值,就改變臨界值為Integer.MAX_VALUE,返回舊陣列
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
//1.新陣列擴容1倍後小於陣列最大值 2.舊陣列長度大於預設初始值
//就把新陣列的臨界值也擴大一倍
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
//如果舊陣列為空,且臨界值大於0,新陣列長度就等於舊陣列的擴容臨界值
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
//舊陣列為空,臨界值≤0,新陣列長度和臨界值都使用預設值
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//如果新陣列臨界值還為0,就設定根據loadFactor(負載因子)臨界值(其實好像不可能發生?上面的情況要不就return,要不都有賦值)
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
//更新裝填因子
threshold = newThr;
//建表
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
//對新表的初始化
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
//如果當前節點沒有雜湊衝突直接插入就行
if (e.next == null)
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 = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
//遍歷當前雜湊衝突連結串列
do {
next = e.next;
//用於計算擴容新增後計算位置bit如果為0,就保持原位置(陣列標識)
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
//如果新增bit後計算位置bit為1,就是原位置+舊陣列長度(陣列標識)
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
**擴容後新結點的位置判斷:if(e.hash & oldCap == 0){}
- n-1:n是當前tab的長度
- hash(key1):key1的hash值
- hash(key2):key2的hash值
可以看出擴容之後(b)只有部分資料(key2)的陣列index會發生改變,key1保持不變
而發生變化的就是hash(key)&oldCap這一位bit,所以只需要判斷這一位就知道位置是否變化
而位置變化的大小就是原位置+oldCap
③public V get(Object key)
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;
//判斷必要欄位是否已經初始化,如果沒有直接返回null
if ((tab = table) != null && (n = tab.length) > 0 &&
//注意這裡對first的賦值,插入時也是用長度和hash進行與運算來得出tab位置
(first = tab[(n - 1) & hash]) != null) {
//首先都是檢查首結點
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
//存在雜湊衝突狀況
if ((e = first.next) != null) {
//判斷是不是紅黑樹
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
//不是紅黑樹就當做連結串列來處理(key和hash相同就返回)
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
④public boolean containsKey(Object Key)
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//先判斷是否為空,然後拿到hash相同的頭結點
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//先檢查頭結點是否key也和傳進來的key相同
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
//如果和頭結點不相同,繼續往深查詢
if ((e = first.next) != null) {
//紅黑樹查詢
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
//連結串列查詢
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
⑤public V remove(Object key)
public boolean remove(Object key, Object value)
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
//還是先檢查必要的引數table和table.length,index是所要刪除元素的陣列下標
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e; K k; V v;
//還是先檢查頭結點的key和hash是否和要求一致,如果一致就賦值node指向要刪除的結點
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
//如果不一致,可能在頭結點的後面,往深找
else if ((e = p.next) != null) {
//如果是紅黑樹結構,就呼叫getTreeNode()來找結點返回給node
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
//如果是連結串列結構的處理,找到結點就賦值給node。迴圈中以及迴圈外p.next=e
//如果第一次執行就break了,代表只有一個節點?
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
//如果node不為空,說明找到了要刪除的結點
//matchValue為是否需要對比value值(remove兩個方法不同值)
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
//紅黑樹結構刪除
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
//如果頭結點就是所要刪除結點
else if (node == p)
tab[index] = node.next;
//node不是首節點:p是node的父節點,直接改變一下指標即可
else
p.next = node.next;
//HashMap的操作次數+1
++modCount;
--size;
//讓子類LinkedHashMap重寫,HashMap中沒有實現
afterNodeRemoval(node);
return node;
}
}
return null;
}
LinkedHashMap(雜湊表+非迴圈雙向連結串列)[1.8不是迴圈]{有序}
Node(Entry):
這裡的prev就是before,next就是after,hnext就是next(原HashMap中解決雜湊衝突的指標)
*圖中省略了Key/Value
*head和tail只有一個結點時指向同一個結點
*accessOrder為true:連結串列根據訪問順序排序(LRU,最新訪問放後面);為false:連結串列根據插入順序排序
① public V put(K key, V value)
LinkedHashMap並沒有重寫put方法,但是重寫了構造結點的幾個方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
...
tab[i] = newNode(hash, key, value, null);
...
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
...
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
...
afterNodeAccess(e);
...
afterNodeInsertion(evict);
return null;
}
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
//集合之前是空的
if (last == null)
head = p;
else {
//將新節點連結到尾部
p.before = last;
last.after = p;
}
}
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
②public V get(Object key)
void afterNodeAccess(Node<K,V> e)
*getNode(int hash,Object key)和HashMap的實現一樣,就不重複了
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
//如果是訪問順序排序,才進行排序;如果最後一個結點已經是e就不用排序了
if (accessOrder && (last = tail) != e) {
//用p指向e
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
//情況1:p前一個結點b不存在
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
//情況2:p後一個結點a不存在
else
last = b;
//last==null代表只有一個結點p
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
//新的尾結點變成p
tail = p;
++modCount;
}
}
③public V remove(Object key)
void afterNodeRemoval(Node<K,V> e)
*getNode(int hash,Object key)和HashMap的實現一樣,就不重複了
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
//刪除節點後日常兩種情況:前面結點是否空,後面結點是否空
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
TreeMap(紅黑樹){有序}
可以對新增進來的元素進行排序[二叉搜尋樹]
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
//比較器
private final Comparator<? super K> comparator;
//根結點
private transient Entry<K,V> root;
/**
* 結點個數
*/
private transient int size = 0;
/**
* 樹結構被修改次數
*/
private transient int modCount = 0;
//紅黑樹節點顏色
private static final boolean RED = false;
private static final boolean BLACK = true;
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
}
}
①public V put(K key, V value)
public V put(K key, V value) {
Entry<K,V> t = root;
//如果根結點為空,就設定插入的元素為根結點root,設定必要引數size/modCount後返回
if (t == null) {
//....
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
//提取出比較器
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
//如果比較器不為空,就採用比較器裡面的排序演算法進行排序
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
//排序結果小於0:新增節點的key小於當前根結點,往左子樹找插入位置
if (cmp < 0)
t = t.left;
//排序結果大於0:新增節點的key大於當前根結點,往右子樹找插入位置
else if (cmp > 0)
t = t.right;
//兩個key相同,新值覆蓋舊值,返回舊值
else
return t.setValue(value);
} while (t != null);
}
//如果比較器為空,就採用預設的排序演算法
else {
if (key == null)
throw new NullPointerException();
//獲得比較器
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
//和上面處理步驟一樣
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
//執行到這裡,代表key沒有重複,需要新插入結點,所以指定parent為其父節點
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
//插入完引數設定
size++;
modCount++;
return null;
}
② final int compare(Object k1, Object k2)
/**
* Compares two keys using the correct comparison method for this TreeMap.
* 通過正確的比較器來比較兩個key:如果TreeMap比較器為空,就用key類實現的比較器;如果不為空,就直接用來比較
*/
@SuppressWarnings("unchecked")
final int compare(Object k1, Object k2) {
//如果
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
③private void fixAfterInsertion(Entry<K,V> x)
/** From CLR */
//CLR:公共語言執行庫和Java虛擬機器一樣也是一個執行時環境,它負責資源管理(記憶體分配和垃圾收集等),並保證應用和底層作業系統之間必要的分離
private void fixAfterInsertion(Entry<K,V> x) {
//新增的結點設定成紅色
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}
④public V get(Object key)
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
//如果有自定義比較器物件,分離出來執行另一段邏輯相同程式碼(為了效能)
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
//提出當前key的型別實現的比較器k
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
//深度遍歷
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
/**
* Version of getEntry using comparator. Split off from getEntry
* for performance. (This is not worth doing for most methods,
* that are less dependent on comparator performance, but is
* worthwhile here.)
* 為了效能將此方法從getEntry()分離出來,大部分方法不依賴於效能的話就沒什麼意義,反之則有意義
*/
final Entry<K,V> getEntryUsingComparator(Object key) {
//邏輯也是差不多的,執行此方法代表有自定義的比較器
@SuppressWarnings("unchecked")
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
}
return null;
}
⑤public V remove(Object key)
public V remove(Object key) {
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
deleteEntry(p);
return oldValue;
}
/**
* Delete node p, and then rebalance the tree.
*/
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
// If strictly internal, copy successor's element to p and then make p
// point to successor.
if (p.left != null && p.right != null) {
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children
// Start fixup at replacement node, if it exists.
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {
// Link replacement to parent
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
// Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null;
// Fix replacement
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
root = null;
} else { // No children. Use self as phantom replacement and unlink.
if (p.color == BLACK)
fixAfterDeletion(p);
if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
HashTable(陣列+連結串列)[Synchronized實現執行緒安全-鎖住整個table]
key-value都不能為null
Hashtable 中的方法是Synchronize的,而HashMap中的方法在預設情況下是非Synchronize的。在多執行緒併發的環境下,可以直接使用Hashtable
Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但二者都實現了Map介面
基本的引數都是一樣的,引數名從Node改成了Entry
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
/**
* The hash table data.
* 雜湊表資料用一個陣列存起來
*/
private transient Entry<?,?>[] table;
/**
* The total number of entries in the hash table.
* 資料總數
*/
private transient int count;
/**
* The table is rehashed when its size exceeds this threshold. (The
* value of this field is (int)(capacity * loadFactor).)
* 擴容臨界值
* @serial
*/
private int threshold;
/**
* The load factor for the hashtable.
* 擴容因子
* @serial
*/
private float loadFactor;
/**
* The number of times this Hashtable has been structurally modified
* Structural modifications are those that change the number of entries in
* the Hashtable or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the Hashtable fail-fast. (See ConcurrentModificationException).
* 操作次數
*/
private transient int modCount = 0;
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
}
}
①public synchronized V put(K key, V value)
//HashTable不同於HashMap:使用synchronized關鍵字保證執行緒同步
public synchronized V put(K key, V value) {
// Make sure the value is not null
//HashTable不同於HashMap:不接受value為null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
//返回物件的32位jvm記憶體地址
//HashTable不同於HashMap:不接受key為null,隱式丟擲空指標異常
int hash = key.hashCode();
//HashTable不同於HashMap:HashMap陣列下標計算i = (n - 1) & hash]
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//遍歷連結串列。新值覆蓋舊值,返回舊值
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
//沒有相同index又相同key,呼叫方法addEntry()進行判斷是否擴容然後插入
addEntry(hash, key, value, index);
return null;
}
//執行此函式代表要多一個結點了
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
//判斷是否需要擴容(是否≥臨界值)
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
/**
* Increases the capacity of and internally reorganizes this
* hashtable, in order to accommodate and access its entries more
* efficiently. This method is called automatically when the
* number of keys in the hashtable exceeds this hashtable's capacity
* and load factor.
* 用於重新hash
*/
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//和HashMap不一樣的地方:設定的新容量是*2+1
int newCapacity = (oldCapacity << 1) + 1;
//如果擴容之後,已經大於陣列的最大值了(Integer.MAX_VALUE - 8)
if (newCapacity - MAX_ARRAY_SIZE > 0) {
//如果舊陣列已經擴容到最大就繼續用
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
//還沒到最大新陣列容量就擴容成最大
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
//遍歷舊陣列的每個Entry
for (int i = oldCapacity ; i-- > 0 ;) {
//遍歷雜湊衝突中連結串列的每個數
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
//重新計算陣列下標後插入,如果又是同一條連結串列,插入的順序和之前的相反
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
②public synchronized V get(Object key)
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key.equals(k))},
* then this method returns {@code v}; otherwise it returns
* {@code null}. (There can be at most one such mapping.)
*
* @param key the key whose associated value is to be returned
* @return the value to which the specified key is mapped, or
* {@code null} if this map contains no mapping for the key
* @throws NullPointerException if the specified key is null
* @see #put(Object, Object)
*/
@SuppressWarnings("unchecked")
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
//很普通地求出陣列下標
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
//很普通地遍歷然後找key和hash都相同的value
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
//很普通地找不到返回null
return null;
}
ConcurrentMap
ConcurrentHashMap
*JDK1.7(獲取的是Segment鎖)
多執行緒環境下:並不需要爭奪同一個鎖,HashTable的話效率太低(鎖住整個table)
整個 Hash 表被分成多個段,每個段中會對應一個 Segment 段鎖,段與段之間可以併發訪問,但是多執行緒想要操作同一個段是需要獲取鎖的。所有的 put,get,remove 等方法都是根據鍵的 hash 值對應到相應的段中,然後嘗試獲取鎖進行訪問。
*JDK1.8
取消了基於 Segment 的分段鎖思想,改用 CAS + synchronized 控制併發操作,在某些方面提升了效能。並且追隨 1.8 版本的 HashMap 底層實現,使用陣列+連結串列或紅黑樹進行資料儲存。
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
/**
* The largest possible table capacity. This value must be
* exactly 1<<30 to stay within Java array allocation and indexing
* bounds for power of two table sizes, and is further required
* because the top two bits of 32bit hash fields are used for
* control purposes.
*/
//整個Node陣列的最大範圍,最高的兩位bits要用於控制
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The default initial table capacity. Must be a power of 2
* (i.e., at least 1) and at most MAXIMUM_CAPACITY.
*/
//預設的Node陣列容量,必須是2次冪
private static final int DEFAULT_CAPACITY = 16;
/**
* The largest possible (non-power of two) array size.
* Needed by toArray and related methods.
*/
//陣列的最大容量(不是Node陣列),要留8個位元組控制陣列物件
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* The default concurrency level for this table. Unused but
* defined for compatibility with previous versions of this class.
*/
//預設併發級別:此版本已經不用,是為了相容以前版本
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
* The load factor for this table. Overrides of this value in
* constructors affect only the initial table capacity. The
* actual floating point value isn't normally used -- it is
* simpler to use expressions such as {@code n - (n >>> 2)} for
* the associated resizing threshold.
*/
//載入因子,實際上不用浮點數,而是用n - (n >>> 2)來調整閾值
private static final float LOAD_FACTOR = 0.75f;
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2, and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
//桶裡已有8個資料,再加的時候就要轉換成紅黑樹
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
//小於6時,將紅黑樹轉回連結串列
static final int UNTREEIFY_THRESHOLD = 6;
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* The value should be at least 4 * TREEIFY_THRESHOLD to avoid
* conflicts between resizing and treeification thresholds.
*/
//樹化所要求的陣列最小長度,要樹化陣列長度要大於此值,不滿足則呼叫tryPresize(int size)擴容
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* Minimum number of rebinnings per transfer step. Ranges are
* subdivided to allow multiple resizer threads. This value
* serves as a lower bound to avoid resizers encountering
* excessive memory contention. The value should be at least
* DEFAULT_CAPACITY.
*/
private static final int MIN_TRANSFER_STRIDE = 16;
/**
* The number of bits used for generation stamp in sizeCtl.
* Must be at least 6 for 32bit arrays.
*/
//生成sizeCtl需要的位數
private static int RESIZE_STAMP_BITS = 16;
/**
* The maximum number of threads that can help resize.
* Must fit in 32 - RESIZE_STAMP_BITS bits.
*/
//允許的進行擴容的最大程式數
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
/**
* The bit shift for recording size stamp in sizeCtl.
*/
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
*/
//整個雜湊表(陣列形式儲存),Node陣列
transient volatile Node<K,V>[] table;
/**
* The next table to use; non-null only while resizing.
*/
//連線表,用於雜湊表擴容,擴容完成之後重置回null
private transient volatile Node<K,V>[] nextTable;
/**
* Base counter value, used mainly when there is no contention,
* but also as a fallback during table initialization
* races. Updated via CAS.
*/
//儲存雜湊表中儲存所有節點個數,類似於HashMap的size
private transient volatile long baseCount;
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
*/
//-1 :代表table正在初始化,其他執行緒應該交出CPU時間片
//-N: 表示正有N-1個執行緒執行擴容操作(高 16 位是 length 生成的識別符號,低 16 位是擴容的執行緒數)
//大於 0: 如果table已經初始化,代表table容量,預設為table大小的0.75,如果還未初始化,代表需要初始化的大小
private transient volatile int sizeCtl;
}
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
建構函式
/**
* Creates a new, empty map with the default initial table size (16).
*/
//建立預設初始容量16,載入因子0.75,併發等級16的空對映
public ConcurrentHashMap() {
}
/**
* Creates a new, empty map with an initial table size
* accommodating the specified number of elements without the need
* to dynamically resize.
*
* @param initialCapacity The implementation performs internal
* sizing to accommodate this many elements.
* @throws IllegalArgumentException if the initial capacity of
* elements is negative
*/
//建立指定初始容量,載入因子0.75,併發等級16的空對映
public ConcurrentHashMap(int initialCapacity) {
if (initialCapacity < 0) //合法性檢查
throw new IllegalArgumentException();
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
//找到最接近initialCapacity的2次冪
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
this.sizeCtl = cap; //sizeCtl > 0時相當於擴容閾值
}
/**
* Creates a new map with the same mappings as the given map.
*
* @param m the map
*/
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
this.sizeCtl = DEFAULT_CAPACITY;
putAll(m);
}
/**
* Creates a new, empty map with an initial table size based on
* the given number of elements ({@code initialCapacity}) and
* initial table density ({@code loadFactor}).
*
* @param initialCapacity the initial capacity. The implementation
* performs internal sizing to accommodate this many elements,
* given the specified load factor.
* @param loadFactor the load factor (table density) for
* establishing the initial table size
* @throws IllegalArgumentException if the initial capacity of
* elements is negative or the load factor is nonpositive
*
* @since 1.6
*/
//創造指定初始容量和載入因子,以及併發等級為1的對映
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, 1);
}
/**
* Creates a new, empty map with an initial table size based on
* the given number of elements ({@code initialCapacity}), table
* density ({@code loadFactor}), and number of concurrently
* updating threads ({@code concurrencyLevel}).
*
* @param initialCapacity the initial capacity. The implementation
* performs internal sizing to accommodate this many elements,
* given the specified load factor.
* @param loadFactor the load factor (table density) for
* establishing the initial table size
* @param concurrencyLevel the estimated number of concurrently
* updating threads. The implementation may use this value as
* a sizing hint.
* @throws IllegalArgumentException if the initial capacity is
* negative or the load factor or concurrencyLevel are
* nonpositive
*/
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) //引數合法性檢查
throw new IllegalArgumentException();
//有多少個桶就允許多少個併發程式
if (initialCapacity < concurrencyLevel) // Use at least as many bins
initialCapacity = concurrencyLevel; // as estimated threads
//size是容量,不是閾值
long size = (long)(1.0 + (long)initialCapacity / loadFactor);
//構造時sizeCtl為2次冪(此時sizeCtl是容量,不是閾值?)
int cap = (size >= (long)MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY : tableSizeFor((int)size);
this.sizeCtl = cap;
}
①public V put(K key, V value)
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
//ConcurrentHashMap不允許key或者value為空
if (key == null || value == null) throw new NullPointerException();
//hash的計算:return (h ^ (h >>> 16)) & HASH_BITS;
//(h是key.hashCode()、HASH_BITS預設是0x7fffffff)
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//檢查是否需要初始化(tab為空)
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//根據鍵的hash值找到陣列相應的index
//如果表不為空、hash值對應的桶為空就用CAS無鎖式向該位置新增一個結點
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//檢測到結點的hash值為MOVED,表示正在進行陣列擴張的資料複製階段
//允許多執行緒複製,所以當前執行緒去幫忙複製(轉移)
else if ((fh = f.hash) == MOVED)
//結點轉移
tab = helpTransfer(tab, f);
else {
V oldVal = null;
//鎖住桶
synchronized (f) {
//i為該桶在Node陣列的下標值,f為對應的桶
if (tabAt(tab, i) == f) {
//fh為f桶的hash值
if (fh >= 0) {
binCount = 1;
//遍歷f桶內所有元素
for (Node<K,V> e = f;; ++binCount) {
K ek;
//f桶內有key相同的鍵值對,返回一個oldVal舊值
//onlyIfAbsent = false(put操作),才將舊值改成新值
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
//f桶內沒有key相同鍵值對,插入一個新節點
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
//如果該結點是紅黑樹
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
//返回舊值oldVal、onlyIfAbsent==false時:替換舊值
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
//連結串列深度超過8就轉換成紅黑樹
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
/**
* Initializes table, using the size recorded in sizeCtl.
*/
//table為空時初始化table
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
//sizeCtl < 0 代表有程式正在初始化table,讓權等待
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
//比較sizeCtl是否和sc相同,如果是就用-1賦值sizeCtl
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
//初始化時,sc=sizeCtl是陣列的容量而不是閾值
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
//新生節點陣列,大小為n
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
//擴容閾值設定為3n/4(也就相當於0.75 x Capacity = threshold)
sc = n - (n >>> 2);
}
} finally {
//此時sizeCtl才從容量變為閾值(初始化之後)
sizeCtl = sc;
}
break;
}
}
return tab;
}
//通過unsafe物件反射獲取桶?i為Node陣列中桶對應的下標
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
//CAS操作:判斷i下標的桶是不是c,如果是就用v換c,否則不換
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
協助擴容:
/**
* Helps transfer if a resize is in progress.
*/
//協助擴容
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
//由於是協助擴容,所以當前nextTable應該不為空的
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
//根據table長度獲得一個識別符號
int rs = resizeStamp(tab.length);
//如果nextTable和Table沒有被併發修改,而且處於擴容狀態
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
// 如果 sizeCtl 無符號右移 16 不等於 rs ( sc前 16 位如果不等於識別符號,則識別符號變化了)
// 或者 sizeCtl == rs + 1 (擴容結束了,沒有執行緒在進行擴容)
//(預設第一個執行緒設定 sc ==rs 左移 16 位 + 2,當第一個執行緒結束擴容了,就會將 sc 減一。這個時候,sc 就等於 rs + 1)
// 或者 sizeCtl == rs + 65535 (如果達到最大幫助執行緒的數量,即 65535)
// 或者轉移下標正在調整 (擴容結束)
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
//以上情況都不是,CAS操作將sizeCtl+1,代表多了當前執行緒來幫助擴容
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
//進行轉移
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
//根據table長度獲得一個識別符號
int rs = resizeStamp(tab.length);
第一個程式在擴容時
SC的值被設定成:識別符號(高16位)+2(低16位)。也就是rs左移16位+2
當所有程式完成擴容時
SC的值應該是:識別符號(高16位)+1(低16位)
滿足四條中 任 一 一 條 當前執行緒就不幫忙轉換結點到nextTable
- 如果 sizeCtl 無符號右移 16 不等於 rs ( sc前 16 位如果不等於識別符號,則識別符號變化了,就代表還未擴容)
sizeCtl == rs + 1 (擴容結束了,沒有執行緒在進行擴容)(預設第一個執行緒設定 sc ==rs 左移 16 位 + 2,當第一個執行緒結束擴容了,就會將 sc 減一。這個時候,sc 就等於 rs + 1)或者 sizeCtl == rs + 65535 (如果達到最大幫助執行緒的數量,即 65535)- 或者轉移下標正在調整 (擴容結束)
if ((sc >>> RESIZE_STAMP_SHIFT) != rs ||
sc == rs + 1 ||
sc == rs + MAX_RESIZERS ||
transferIndex <= 0)
break;
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8214427
但是根據這裡提交的bug,
所以第二、三個判斷是沒有用的。
/**
* Moves and/or copies the nodes in each bin to new table. See
* above for explanation.
*/
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
int n = tab.length, stride;
//限制每個CPU處理陣列長度最少為16
//即如果有一個陣列長度只有16,則處理此陣列的執行緒只能有一個
//stride為每個執行緒處理的陣列長度
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE; // subdivide range
if (nextTab == null) { // initiating
try {
//初始化nextTable:新建一個大小為原Table兩倍的新Table
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
sizeCtl = Integer.MAX_VALUE;
return;
}
nextTable = nextTab;
transferIndex = n;
}
int nextn = nextTab.length;
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
boolean advance = true;
//nextTable是否完成掃描的標識(false未完成、true已完成)
boolean finishing = false; // to ensure sweep before committing nextTab
for (int i = 0, bound = 0;;) {
Node<K,V> f; int fh;
while (advance) {
int nextIndex, nextBound;
//如果已經完成掃描,就不用這一步了
if (--i >= bound || finishing)
advance = false;
//需要轉換的陣列下標不合法
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
//CAS操作告訴執行緒要處理到哪個陣列,下標為i
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
bound = nextBound;
i = nextIndex - 1;
advance = false;
}
}
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
//完成掃描了,將舊陣列指向新陣列,清空臨時物件nextTable
if (finishing) {
nextTable = null;
table = nextTab;
//擴容閾值設定成原來的1.5倍
sizeCtl = (n << 1) - (n >>> 1);
return;
}
//詳見上面關於rs和sc的分析
//如果sc = sc - 1:代表當前執行緒執行完成,退出
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
//還有第一個執行緒在擴容,就直接表示任務完成??
finishing = advance = true;
i = n; // recheck before commit
}
}
//如果遍歷到的桶為空,就放入一個fwd(ForwardingNode)結點,表示這個桶已經被處理過了
else if ((f = tabAt(tab, i)) == null)
advance = casTabAt(tab, i, null, fwd);
//併發控制核心:如果遍歷到fwd指標,就代表這個桶已經被處理過了,跳過。
else if ((fh = f.hash) == MOVED)
advance = true; // already processed 已經 處理
//處理桶
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
Node<K,V> ln, hn;
//如果獲得的桶是連結串列結構
if (fh >= 0) {
//處理同HashMap,resize後的結點要不留在原地,要不就移動n個桶
//(n是原來的capacity),詳情可以往上拉看HashMap中分析
int runBit = fh & n;
Node<K,V> lastRun = f;
//遍歷連結串列結點,拿到連結串列尾的結點lastRun
//或者結點鏈[上面所有結點都保持原地址或者移動capacity個單位]
//runBit是lastRun是否移動的判斷
//如果要保持原序插入的話,倒過來是最好的,可以省掉一個記錄頭結點的空間
for (Node<K,V> p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
if (runBit == 0) {
ln = lastRun;
hn = null;
}
else {
hn = lastRun;
ln = null;
}
//遍歷連結串列的每個結點根據新地址值來插入不同的連結串列
for (Node<K,V> p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
//ln = ln(new).next / hn = hn(new).next
//ln是resize後原地不動的連結串列
if ((ph & n) == 0)
ln = new Node<K,V>(ph, pk, pv, ln);
//hn是resize後移動capacity單位的連結串列
else
hn = new Node<K,V>(ph, pk, pv, hn);
}
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
else if (f instanceof TreeBin) {
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> lo = null, loTail = null;
TreeNode<K,V> hi = null, hiTail = null;
int lc = 0, hc = 0;
for (Node<K,V> e = t.first; e != null; e = e.next) {
int h = e.hash;
TreeNode<K,V> p = new TreeNode<K,V>
(h, e.key, e.val, null, null);
if ((h & n) == 0) {
if ((p.prev = loTail) == null)
lo = p;
else
loTail.next = p;
loTail = p;
++lc;
}
else {
if ((p.prev = hiTail) == null)
hi = p;
else
hiTail.next = p;
hiTail = p;
++hc;
}
}
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
(hc != 0) ? new TreeBin<K,V>(lo) : t;
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin<K,V>(hi) : t;
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
}
}
}
}
}
/**
* Finds or adds a node.
* @return null if added
*/
final TreeNode<K,V> putTreeVal(int h, K k, V v) {
Class<?> kc = null;
boolean searched = false;
for (TreeNode<K,V> p = root;;) {
int dir, ph; K pk;
if (p == null) {
first = root = new TreeNode<K,V>(h, k, v, null, null);
break;
}
else if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
return p;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
if (!searched) {
TreeNode<K,V> q, ch;
searched = true;
if (((ch = p.left) != null &&
(q = ch.findTreeNode(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.findTreeNode(h, k, kc)) != null))
return q;
}
dir = tieBreakOrder(k, pk);
}
TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
TreeNode<K,V> x, f = first;
first = x = new TreeNode<K,V>(h, k, v, f, xp);
if (f != null)
f.prev = x;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
if (!xp.red)
x.red = true;
else {
lockRoot();
try {
root = balanceInsertion(root, x);
} finally {
unlockRoot();
}
}
break;
}
}
assert checkInvariants(root);
return null;
}
/**
* Replaces all linked nodes in bin at given index unless table is
* too small, in which case resizes instead.
*/
//將連結串列中的Node變為TreeNode
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
//判斷陣列長度是否小於最小樹化的陣列數量要求(table太小不能樹化,要先擴容)
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
//擴容以避免一個桶中結點過多
tryPresize(n << 1);
//開始樹化
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
//操作之前鎖住當前桶
synchronized (b) {
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;
//遍歷所有結點,將所有Node變為TreeNode
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
//只是用TreeNode封裝,沒有用到left/right/parent等屬性
if ((p.prev = tl) == null)
hd = p;
else
tl.next = p;
tl = p;
}
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
putVal()---addCount(1L, binCount);
/**
* Adds to count, and if table is too small and not already
* resizing, initiates transfer. If already resizing, helps
* perform transfer if work is available. Rechecks occupancy
* after a transfer to see if another resize is already needed
* because resizings are lagging additions.
*
* @param x the count to add
* @param check if <0, don't check resize, if <= 1 only check if uncontended
*/
//x是要加的個數
//check當前桶已有結點個數,check<0就不用檢查是否需要擴容;check<=1就只檢查是否沒有併發
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
//counterCells用於併發新增數量(有這個物件就代表併發了;沒有不代表沒有併發,還未處理)
//baseCount用於無競爭新增數量(CAS成功代表沒有併發)
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
CounterCell a; long v; int m;
boolean uncontended = true;
//as == null :併發已發生,尚未處理併發
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
//在此方法中繼續死迴圈操作,直到成功
fullAddCount(x, uncontended);
return;
}
if (check <= 1)
return;
s = sumCount();
}
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n);
if (sc < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
}
}
}
②
ConcurrentSkipListMap
相關文章
- 【Java X 原始碼剖析】Collection的原始碼分析-JDK1.8-仍在更新Java原始碼JDK
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- Java LinkedList 原始碼剖析Java原始碼
- sync.Map原始碼分析原始碼
- stl原始碼分析——map/multimap原始碼
- go sync.Map原始碼分析Go原始碼
- 詳解Java 容器(第④篇)——容器原始碼分析 - MapJava原始碼
- Java容器 | 基於原始碼分析Map集合體系Java原始碼
- 【Java集合原始碼剖析】Java集合框架Java原始碼框架
- 整合原始碼深度剖析:Fescar x Spring Cloud原始碼SpringCloud
- Spring原始碼剖析9:Spring事務原始碼剖析Spring原始碼
- jQuery原始碼剖析(三) - Callbacks 原理分析jQuery原始碼
- spark 原始碼分析之十三 -- SerializerManager剖析Spark原始碼
- Faiss原始碼剖析:類結構分析AI原始碼
- Flutter Dio原始碼分析(三)--深度剖析Flutter原始碼
- Java原始碼分析:Guava之不可變集合ImmutableMap的原始碼分析Java原始碼Guava
- Java容器原始碼學習--ArrayList原始碼分析Java原始碼
- java 原始碼分析 —BooleanJava原始碼Boolean
- Java:HashMap原始碼分析JavaHashMap原始碼
- Java String原始碼分析Java原始碼
- 【Java】ServiceLoader原始碼分析Java原始碼
- Java 原始碼如何分析?Java原始碼
- epoll–原始碼剖析原始碼
- Thread原始碼剖析thread原始碼
- Handler原始碼剖析原始碼
- HashMap原始碼剖析HashMap原始碼
- 我的原始碼閱讀之路:redux原始碼剖析原始碼Redux
- Netty4.x原始碼分析Netty原始碼
- 友好 RxJava2.x 原始碼解析(三)zip 原始碼分析RxJava原始碼
- Go語言——sync.Map原始碼分析Go原始碼
- 從原始碼全面剖析 React 元件更新機制原始碼React元件
- 深入Preact原始碼分析(4.20更新)React原始碼
- Java HashMap和Go map原始碼對比JavaHashMapGo原始碼
- 07.ElementUI 2.X 原始碼學習:原始碼剖析之工程化(二)UI原始碼
- 08.ElementUI 2.X 原始碼學習:原始碼剖析之工程化(三)UI原始碼
- 06.ElementUI 2.X 原始碼學習:原始碼剖析之工程化(一)UI原始碼
- Retrofit原始碼分析三 原始碼分析原始碼
- Vert.x原始碼分析之Launcher原始碼