HashMap扩容原理
HashMap扩容原理
在添加元素或初始化的时候需要调用resize
方法进行扩容,第一次添加数据初始化数组长度为16
,以后每次每次扩容都是达到了扩容阈值(数组长度*0.75)。
每次扩容的时候,都是扩容之前容量的2
倍。
扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中。
- 没有hash冲突的节点,则直接使用
e.hash&(newCap-1)
计算新数组的索引位置。 - 如果是红黑树,走红黑树的添加。
- 如果是链表,则需要遍历链表,可能需要拆分链表,判断
e.hash&oldCap
是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上。
JDK1.7多线程扩容死循环
在JDK1.7的hashmap中在数组进行扩容的时候,因为链表是头插法
,在进行数据迁移的过程中,有可能导致死循环。
比如说,现在有两个线程
线程一:读取到当前的hashmap数据,数据中一个链表,在准备扩容时,线程二介入。
线程二:也读取hashmap,直接进行扩容。因为是头插法,链表的顺序会进行颠倒过来。比如原来的顺序是AB,扩容后的顺序是BA,线程二执行结束。
线程一:继续执行的时候就会出现死循环的问题。
线程一先将A移入新的链表,再将B插入到链头,由于另外一个线程的原因,B的next指向了A,所以B->A->B,形成循环。
当然,JDK8将扩容算法做了调整,不再将元素加入链表头(而是保持与扩容前一样的顺序),尾插法
,就避免了JDK1.7中死循环的问题。