HashMap 的resize过程(HashMap扩容原理)

181 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

HashMap在put的时候会先检查当前数组的length,如果插入新的值的时候使得length > 0.75f * size(f 为加载因子,可以在创建hashMap时指定)的话,会将数组进行扩容为当前容量的2倍。扩容之后必定要将原有hashMap 中的值拷贝到新容量的hashMap 里面,HashMap 默认的容量为16,加载因子为0.75,也就是说当HashMap 中Entry的个数超过 16 * 0.75 = 12时, 会将容量扩充为 16 * 2 = 32,然后重新计算元素在数组中的位置,这是一个非常耗时的操作,所以我们在使用HashMap的时候如果能预先知道Map中元素的大小,预设其大小能够提升其性能

resize代码:

//HashMap数组扩容

void resize(int newCapacity) {

Entry[] oldTable = table;

int oldCapacity = oldTable.length;

//如果当前的数组长度已经达到最大值,则不在进行调整

if (oldCapacity == MAXIMUM_CAPACITY) {

threshold = Integer.MAX_VALUE;

return;

}

//根据传入参数的长度定义新的数组

Entry[] newTable = new Entry[newCapacity];

//按照新的规则,将旧数组中的元素转移到新数组中

transfer(newTable);

table = newTable;

//更新临界值

threshold = (int)(newCapacity * loadFactor);

}

//旧数组中元素往新数组中迁移

void transfer(Entry[] newTable) {

//旧数组

Entry[] src = table;

//新数组长度

int newCapacity = newTable.length;

//遍历旧数组

for (int j = 0; j < src.length; j++) {

Entry e = src[j];

if (e != null) {

src[j] = null;

do {

Entry next = e.next;

int i = indexFor(e.hash, newCapacity);//放在新数组中的index位置

e.next = newTable[i];//实现链表结构,新加入的放在链头,之前的的数据放在链尾

newTable[i] = e;

e = next;

} while (e != null);

}

}

}

这是1.7中的代码,1.8中引入了红黑树的概念,代码会相对复杂一些。

 

通俗的讲,如果数组中元素的容量超过阈值0.75,会触发扩容,

扩容是先把源数组放进一个临时数组中,获取老数组的长度,通过老数组长度乘2获取新数组长度,并创建新数组,把临时数组中的数据通过重新计算下表,存进扩容后的数组中.