1. ConcurrentHashMap的put流程:
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException()
int hash = spread(key.hashCode())
int binCount = 0
for (Node<K,V>[] tab = table
Node<K,V> f
if (tab == null || (n = tab.length) == 0)
tab = initTable()
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f)
else {
V oldVal = null
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1
for (Node<K,V> e = f
K ek
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val
if (!onlyIfAbsent)
e.val = value
break
}
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 = p.val
if (!onlyIfAbsent)
p.val = value
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i)
if (oldVal != null)
return oldVal
break
}
}
}
addCount(1L, binCount)
return null
}
- 计算当时元素的hash值
- for循环开始,判断map是否初始化,没有初始化则执行初始化逻辑。(initTable)
- 判断当前桶的首节点是否为null,是则cas自旋插入。
- 判断当前节点的hashcode是否等于MOVE也就是-1,则开始帮助扩容
- 以上条件都不满足,则利用synchronized加锁进行写入数据。
- 如果当前元素数量大于等于8,则进入判断是否需要转变成红黑树。
2. ConcurrentHashMap的get流程:
public V get(Object key) {
Node<K,V>[] tab
int h = spread(key.hashCode())
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val
}
}
return null
}
- 计算当前元素hash值
- 判断当前桶位首节点的hash值与当前get元素的hash是否相等,相等直接返回当前元素
- 判断当前元素的hash是否小于0,小于0则代表正在扩容或者是红黑树,就进行find查找。
- 否则最后进行遍历链表进行查找返回结果
3.HashMap的put流程:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null)
else {
Node<K,V> e
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
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null)
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
p = e
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value
if (!onlyIfAbsent || oldValue == null)
e.value = value
afterNodeAccess(e)
return oldValue
}
}
++modCount
if (++size > threshold)
resize()
afterNodeInsertion(evict)
return null
}
- 计算当前元素的hash值
- 判断当前map是否初始化,没有就进行扩容工作
- 判断当前桶位是否节点为null,是则直接插入
- 如果当前桶位不为空,先判断首节点是否等于当前元素key的hash,是则直接插入。否判断是否是红黑树,是则进行红黑树插入
- 上面都不满足,则进行链表遍历插入,插入使用的是尾插法。
- 遍历做两件事,遍历到最后都不存在进行尾插法插入,顺便判断是否需要转红黑树(当前桶内元素节点是否达到8个)。如果存在当前元素,直接跳出循环
- 如果在当前链表上遍历到当前元素,则进行赋值操作。
- 判断当前容器大小size是否大于阈值,是则进行扩容。
4. HashMap的get流程:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
Node<K,V>[] tab
if ((tab = table) != null && (n = tab.length) > 0 &&
(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)
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e
} while ((e = e.next) != null)
}
}
- 计算当前元素hash值
- 如果当前桶内元素不为空,直接返回该值。
- 如果当前桶是红黑树,则进行红黑树查找
- 以上不满足则进行链表遍历查找返回。
5.HashMap的扩容流程:
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 (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE
return oldTab
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY)
}
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
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
if ((e.hash & oldCap) == 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) {
loTail.next = null
newTab[j] = loHead
}
if (hiTail != null) {
hiTail.next = null
newTab[j + oldCap] = hiHead
}
}
}
}
}
return newTab
}
- 计算出当前map的新容量和新的扩容阈值,默认是2倍。如果当前容量超过最大容量,则不再进行扩容操作
- 初始化新的map
- 进行桶数据重新排放,遍历过程中,会根据hash & oldCap == 0来判断元素存放位置,有两条链表:lo和hi,分别放到老桶和新扩容的桶中,两者相差oldCap。