扩容 resize()
扩容机制
当HashMap中的元素个数超过数组大小(数组长度) * loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_FACTOR)是0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中的元素个数超过16×0.75=12(这个值就是阈值或者边界值threshold值)的时候,就把数组的大小扩展为2×16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预知元素的个数能够有效的提高HashMap的性能。
当HashMap中的其中一个链表的对象个数如果达到了8个,此时如果数组长度没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链表会变成红黑树,节点类型由Node变成TreeNode类型。当然,如果映射关系被移除后,下次执行resize方法时判断树的节点个数低于6,也会再把树转换为链表。
HashMap 扩容重新分配位置原理
在进行扩容的时候,会伴随着一次hash的重新分配,并且会遍历hash表中所有的元素,是非常耗时的,所以在编写程序的时候要尽量避免resize().
而在HashMap在进行扩容的时候,重新计算hash的方式非常的巧妙。因为每次扩容是翻倍操作,也就是原来的容量*2,并且因为计算index值得公式为 (n-1)&hash, hash值不变,影响的只是n的2倍。所以n 的二进制扩容后就是在原来的基础上向左移动了 1为 也就是说 扩容后的 2n-1 的二进制有效位比原来的多一个1 (如:原来n-1的二进制为1111,扩容后则是11111)。所以与相同的hash与计算后,index要么在原来的位置要么是 原来位置+原来的容量值
分析:

结论:
在HashMap进行扩容的时候不需要重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就可以,如果是0,则索引没变 如果是1则索引变成原来位置+旧容量
源码分析
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 
 | 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)
 
 
 newCap = oldThr;
 else {
 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;
 
 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 {
 
 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;
 }
 
 | 
split() 方法说明, ((TreeNode<K,V>)e).split(this, newTab, j, oldCap)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 
 | 
 
 
 
 
 final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
 
 TreeNode<K,V> b = this;
 
 TreeNode<K,V> loHead = null, loTail = null;
 
 TreeNode<K,V> hiHead = null, hiTail = null;
 
 int lc = 0, hc = 0;
 
 for (TreeNode<K,V> e = b, next; e != null; e = next) {
 
 next = (TreeNode<K,V>)e.next;
 
 e.next = null;
 
 if ((e.hash & bit) == 0) {
 if ((e.prev = loTail) == null)
 loHead = e;
 else
 loTail.next = e;
 loTail = e;
 
 ++lc;
 }
 else {
 if ((e.prev = hiTail) == null)
 hiHead = e;
 else
 hiTail.next = e;
 hiTail = e;
 
 ++hc;
 }
 }
 
 if (loHead != null) {
 
 if (lc <= UNTREEIFY_THRESHOLD)
 
 tab[index] = loHead.untreeify(map);
 else {
 
 tab[index] = loHead;
 
 if (hiHead != null)
 
 loHead.treeify(tab);
 }
 }
 
 if (hiHead != null) {
 
 if (hc <= UNTREEIFY_THRESHOLD)
 
 tab[index + bit] = hiHead.untreeify(map);
 else {
 
 tab[index + bit] = hiHead;
 如果低位首节点不为空,说明原来的红黑树已经被拆分成两个链表了
 if (loHead != null)
 
 hiHead.treeify(tab);
 }
 }
 }
 
 
 | 
获取 get()
入口方法 get
| 12
 3
 4
 
 | public V get(Object key) {Node<K,V> e;
 return (e = getNode(hash(key), key)) == null ? null : e.value;
 }
 
 | 
getNode() 方法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 
 | 
 
 
 
 
 
 
 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))))
 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;
 }
 
 
 final TreeNode<K,V> getTreeNode(int h, Object k) {
 return ((parent != null) ? root() : this).find(h, k, null);
 }
 final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
 TreeNode<K,V> p = this;
 do {
 int ph, dir; K pk;
 TreeNode<K,V> pl = p.left, pr = p.right, q;
 if ((ph = p.hash) > h)
 p = pl;
 else if (ph < h)
 p = pr;
 else if ((pk = p.key) == k || (k != null && k.equals(pk)))
 return p;
 else if (pl == null)
 p = pr;
 else if (pr == null)
 p = pl;
 else if ((kc != null ||
 (kc = comparableClassFor(k)) != null) &&
 (dir = compareComparables(kc, k, pk)) != 0)
 p = (dir < 0) ? pl : pr;
 
 else if ((q = pr.find(h, k, kc)) != null)
 return q;
 else
 p = pl;
 } while (p != null);
 return null;
 }
 
 |