之前在高并發(fā)下 的HashMap遇到過的問題介返,第一個是元素多次插入HashMap達到一定飽和度時,隨著HashMap容量增加Key映射位置容易發(fā)生沖突,第二種就是在多線程下出現(xiàn)死循環(huán)圣蝎。擴容有兩步:創(chuàng)建一個新的Entry空數(shù)組刃宵,長度是原數(shù)組的2倍;遍歷原Entry數(shù)組徘公,把所有的Entry重新Hash到新數(shù)組组去。為什么要重新Hash呢?因為長度擴大以后步淹,Hash的規(guī)則也隨之改變。下面是擴容實現(xiàn)诚撵。在多線程下使用ConcurrentHashMap缭裆,ConcurrentHashMap是線程安全的。
1寿烟、final Node<K, V>[] resize() {
? ? // 舊數(shù)組
? ? 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)
? ? ? ? ? ? // 如果舊容量的兩倍小于最大容量并且舊容量大于默認初始容量(16),則容量擴大為兩部筛武,擴容門檻也擴大為兩倍
? ? ? ? ? ? newThr = oldThr << 1; // double threshold
? ? } else if (oldThr > 0) // initial capacity was placed in threshold
? ? ? ? // 使用非默認構造方法創(chuàng)建的map缝其,第一次插入元素會走到這里
? ? ? ? // 如果舊容量為0且舊擴容門檻大于0,則把新容量賦值為舊門檻
? ? ? ? newCap = oldThr;
? ? else {? ? ? ? ? ? ? // zero initial threshold signifies using defaults
? ? ? ? // 調(diào)用默認構造方法創(chuàng)建的map徘六,第一次插入元素會走到這里
? ? ? ? // 如果舊容量舊擴容門檻都是0内边,說明還未初始化過,則初始化容量為默認容量待锈,擴容門檻為默認容量*默認裝載因子
? ? ? ? newCap = DEFAULT_INITIAL_CAPACITY;
? ? ? ? newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
? ? }
? ? if (newThr == 0) {
? ? ? ? // 如果新擴容門檻為0漠其,則計算為容量*裝載因子,但不能超過最大容量
? ? ? ? float ft = (float) newCap * loadFactor;
? ? ? ? newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?
? ? ? ? ? ? ? ? (int) ft : Integer.MAX_VALUE);
? ? }
? ? // 賦值擴容門檻為新門檻
? ? threshold = newThr;
? ? // 新建一個新容量的數(shù)組
? ? @SuppressWarnings({"rawtypes", "unchecked"})
? ? Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
? ? // 把桶賦值為新數(shù)組
? ? table = newTab;
? ? // 如果舊數(shù)組不為空竿音,則搬移元素
? ? if (oldTab != null) {
? ? ? ? // 遍歷舊數(shù)組
? ? ? ? for (int j = 0; j < oldCap; ++j) {
? ? ? ? ? ? Node<K, V> e;
? ? ? ? ? ? // 如果桶中第一個元素不為空和屎,賦值給e
? ? ? ? ? ? if ((e = oldTab[j]) != null) {
? ? ? ? ? ? ? ? // 清空舊桶,便于GC回收?
? ? ? ? ? ? ? ? oldTab[j] = null;
? ? ? ? ? ? ? ? // 如果這個桶中只有一個元素春瞬,則計算它在新桶中的位置并把它搬移到新桶中
? ? ? ? ? ? ? ? // 因為每次都擴容兩倍柴信,所以這里的第一個元素搬移到新桶的時候新桶肯定還沒有元素
? ? ? ? ? ? ? ? if (e.next == null)
? ? ? ? ? ? ? ? ? ? newTab[e.hash & (newCap - 1)] = e;
? ? ? ? ? ? ? ? else if (e instanceof TreeNode)
? ? ? ? ? ? ? ? ? ? // 如果第一個元素是樹節(jié)點,則把這顆樹打散成兩顆樹插入到新桶中去
? ? ? ? ? ? ? ? ? ? ((TreeNode<K, V>) e).split(this, newTab, j, oldCap);
? ? ? ? ? ? ? ? else { // preserve order
? ? ? ? ? ? ? ? ? ? // 如果這個鏈表不止一個元素且不是一顆樹
? ? ? ? ? ? ? ? ? ? // 則分化成兩個鏈表插入到新的桶中去
? ? ? ? ? ? ? ? ? ? // 比如宽气,假如原來容量為4随常,3、7抹竹、11线罕、15這四個元素都在三號桶中
? ? ? ? ? ? ? ? ? ? // 現(xiàn)在擴容到8,則3和11還是在三號桶窃判,7和15要搬移到七號桶中去
? ? ? ? ? ? ? ? ? ? // 也就是分化成了兩個鏈表
? ? ? ? ? ? ? ? ? ? Node<K, V> loHead = null, loTail = null;
? ? ? ? ? ? ? ? ? ? Node<K, V> hiHead = null, hiTail = null;
? ? ? ? ? ? ? ? ? ? Node<K, V> next;
? ? ? ? ? ? ? ? ? ? do {
? ? ? ? ? ? ? ? ? ? ? ? next = e.next;
? ? ? ? ? ? ? ? ? ? ? ? // (e.hash & oldCap) == 0的元素放在低位鏈表中
? ? ? ? ? ? ? ? ? ? ? ? // 比如钞楼,3 & 4 == 0
? ? ? ? ? ? ? ? ? ? ? ? if ((e.hash & oldCap) == 0) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? if (loTail == null)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loHead = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail.next = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail = e;
? ? ? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? // (e.hash & oldCap) != 0的元素放在高位鏈表中
? ? ? ? ? ? ? ? ? ? ? ? ? ? // 比如,7 & 4 != 0
? ? ? ? ? ? ? ? ? ? ? ? ? ? if (hiTail == null)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiHead = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail.next = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail = e;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? } while ((e = next) != null);
? ? ? ? ? ? ? ? ? ? // 遍歷完成分化成兩個鏈表了
? ? ? ? ? ? ? ? ? ? // 低位鏈表在新桶中的位置與舊桶一樣(即3和11還在三號桶中)
? ? ? ? ? ? ? ? ? ? if (loTail != null) {
? ? ? ? ? ? ? ? ? ? ? ? loTail.next = null;
? ? ? ? ? ? ? ? ? ? ? ? newTab[j] = loHead;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? // 高位鏈表在新桶中的位置正好是原來的位置加上舊容量(即7和15搬移到七號桶了)
? ? ? ? ? ? ? ? ? ? if (hiTail != null) {
? ? ? ? ? ? ? ? ? ? ? ? hiTail.next = null;
? ? ? ? ? ? ? ? ? ? ? ? newTab[j + oldCap] = hiHead;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? return newTab;
}
1袄琳、如果使用是默認構造方法询件,則第一次插入元素時初始化為默認值燃乍,容量為16,擴容門檻為12宛琅;
2刻蟹、如果使用的是非默認構造方法,則第一次插入元素時初始化容量等于擴容門檻嘿辟,擴容門檻在構造方法里等于傳入容量向上最近的2的n次方舆瘪;
3、如果舊容量大于0红伦,則新容量等于舊容量的2倍英古,但不超過最大容量2的30次方,新擴容門檻為舊擴容門檻的2倍昙读;
4召调、創(chuàng)建一個新容量的桶;
5蛮浑、搬移元素唠叛,原鏈表分化成兩個鏈表,低位鏈表存儲在原來桶的位置沮稚,高位鏈表搬移到原來桶的位置加舊容量的位置艺沼;