




- HashMap扩容的时机是: 当前HashMap中存放的元素个数 > HashMap的当前总长度 * loadFactor(loadFactor默认值是0.75) 时进行扩容,扩容之后的长度是当前长度的2倍(比如现在存放了 16 * 0.75 = 12个,再存放第十三个元素的时候,HashMap就会自动的扩容,扩展成 2 * 16 = 32个 )。
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
int s = m.size();
float ft = ((float)s / loadFactor) + 1.0F;
- 当扩容之后,需要重新计算元素的位置,这是一个非常耗时的操作,所以我们最好能预设长度,比如我们有1000个元素,那么 new HashMap(1000)? 不!前面我们说过了,应该设置成2的幂,所以 new HashMap(1024)? 其实我们设置成1000也会被调整成1024的,但是这样就可以了吗?不!当元素超过 1024*0.75 = 768个的时候会进行自动扩容,所以我们应该是new HashMap(2048)。


1.新建Entry,长度是原长度的两倍
2.rehash,遍历原有的数组,把那些元素重新映射到新的数组上(参考咱们前面的公式,length变了,结果自然就变了)。
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如下,如果再插入一个元素就会进行扩容
微信公众号:JavaWeb架构师
2.现在有线程A、B同时执行对这个HashMap的元素插入操作

3.完成插入之后,A、B线程都要对这个HashMap进行扩容

4.首先线程A执行

当线程A对原HashMap遍历到了PHP位置的时候,刚刚执行完上面红框部分的代码,这时候:
e = PHP
next = Java
PHP.next = Java
Java.next = null
5.这时候线程A被挂起,线程B完成了所有的操作

Java.next = PHP
PHP.next = null
6.线程A恢复执行-下标值

i的值和线程B中PHP链表的下标一样(参数都是一样,结果就是一样的)
7.线程A恢复执行-PHP第一次
当前状态:
e = PHP
next = Java
Java.next = PHP
PHP.next = null

这时候PHP被放到了线程A的新HashMap中

现在:
e = Java
next = Java
Java.next = PHP
PHP.next = null
8.线程A恢复执行-Java第一次

当前状态:
e = Java
next = PHP
Java.next = PHP
PHP.next = null
代码再次执行到了:

这时候Java被放到了线程A的HashMap的i位置的表头

现在:
e = PHP
next = PHP
Java.next = PHP
PHP.next = null
9.线程A恢复执行-PHP第二次(出现问题)

当前状态:
e = PHP
next = null

当前状态:
PHP.next = Java
Java.next = PHP
e = null





其它
-
欢迎加入交流群:451826376
-
更多信息:www.itcourse.top
网友评论