- 扩容
扩容是指当容器中元素的数量达到某个阈值时,容器自己进行的容量翻倍的操作。 - JDK1.7 HashMap 扩容方法
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;
}
}
}
- 扩容逻辑
假设原容器中有两个元素a
和b
,此时原容器为 table,如下图:
根据上面的扩容逻辑,循环遍历 table 中的元素,循环刚开始时,e 指向第一个元素a
,next 指向b
,如下图:
假设扩容后元素存入下标为 3 的位置,执行e.next = newTable[i]
后,变化如下:
由于新容器还没有任何元素,因此newTable[i]
为空,因此a
的 next 指向了 null。
继续执行newTable[i] = e
,此时新容器中 i 位置指向了a
如下图:
注意:此时新容器和老容器中的a
是同一个对象,存在堆中,因此无论是在新容器还是老容器中对a
进行修改,两个容器都是可见的。真实指向应如下图,但为了方便看,将两个容器分开展示。
继续执行e = next
,此时 e 和 next 都指向b
如下图:
继续下一轮循环,执行Entry<K,V> next = e.next
,此时 next 指向 null 如下图:
继续执行e.next = newTable[i]
,因为此时 e 即为b
,因此 e.next 即为 b.next,newTable[i] 现在已经有了元素a
,因此 b.next 指向了a
如下图:
继续执行newTable[i] = e
,新容器中 i 位置存入了b
,上一步中 b.next 已经指向了a
,因此新的结构如下图:
实际真实指向应是:
继续执行e = next
,此时 e 和 next 都指向了 null,如下图:
再次进入循环时,由于 e==null 不满足条件,结束循环。
至此扩容完成,可以看到新容器中元素排列顺序正好与旧容器相反,即采用头插法创建的链表。
网友评论