jdk1.7中扩容的主要流程:
1.创建新数组:
首先要计算新数组的长度,将长度 扩大为原来的两倍
resize(2 * table.length);
void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}// 根据新传入的newCapacity创建新Entry数组Entry[] newTable = new Entry[newCapacity];// 用来将原先table的元素全部移到newTable里面,重新计算hash,然后再重新根据hash分配位置transfer(newTable, initHashSeedAsNeeded(newCapacity));// 再将newTable赋值给tabletable = newTable;threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);}
2.转移结点
void transfer(Entry[] newTable, boolean rehash) {int newCapacity = newTable.length;for (Entry<K,V> e : table) {while(null != e) {Entry<K,V> next = e.next;if (rehash) {e.hash = null == e.key ? 0 : hash(e.key);}int i = indexFor(e.hash, newCapacity);e.next = newTable[i];newTable[i] = e;e = next;}}
}
单线程环境下hashmap扩容操作图解:
初始情况:
for (Entry<K,V> e : table) :遍历数组中每个结点e
Entry<K,V> next = e.next; next指向结点e的下一个结点
int i = indexFor(e.hash, newCapacity) 计算结点e在新数组中的位置i
e.next = newTable[i];让结点e的next指向新数组该位置的值newTable[i]
newTable[i] = e; 将新数组第i个位置的值设置为e
e = next; 让指针e指向next指向的结点
最终效果:
多线程环境下hashmap扩容操作图解:
初始情况:
假设此时线程1完成扩容
线程2执行以下步骤
for (Entry<K,V> e : table) :遍历数组中每个结点e
Entry<K,V> next = e.next; next指向结点e的下一个结点
int i = indexFor(e.hash, newCapacity) 计算结点e在新数组中的位置i
e.next = newTable[i];让结点e的next指向新数组该位置的值newTable[i]
newTable[i] = e; 将新数组第i个位置的值设置为e
e = next; 让指针e指向next指向的结点
int i = indexFor(e.hash, newCapacity) 计算结点e在新数组中的位置i
e.next = newTable[i];让结点e的next指向新数组该位置的值newTable[i]
int i = indexFor(e.hash, newCapacity) 计算结点e在新数组中的位置i
e.next = newTable[i];让结点e的next指向新数组该位置的值newTable[i]