JDK1.7hashmap成环原因

JDK1.7hashmap成环原因

1.多线程
2.扩容

hashmap成环原因的代码出现在transfer代码中,看以下代码,transfer(),在实际扩容时候把原来数组中的元素放入新的数组中。

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()方法把原数组中的值放到新数组中
    transfer(newTable, initHashSeedAsNeeded(newCapacity));
    //设置hashmap扩容后为新的数组引用
    table = newTable;
    //设置hashmap扩容新的阈值
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
  }

void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
        	//1, 获取旧表的下一个元素
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            //通过key值的hash值和新数组的大小算出在当前数组中的存放位置
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

举个案例:若map某个桶位置 有a,b ,现要插入c,插入c发生扩容,进入代码中。我们理想头插法插入后的顺序是c,b,a。如图
在这里插入图片描述
结合代码来看,第一个循环,for (Entry<K,V> e : table) 这里遍历旧链表。e即为第一个元素,next为下一个元素。中间三行是为了确定 新链表在数组的一个位置。成环的代码主要是在这最后三行代码, 首先插入是从头开始插入的。

假设线程1执行代码,执行到代码Entry<K,V> next = e.next;执行完这段代码,线程1挂起;线程2执行代码,此时线程2执行成功,将该桶链表顺序更改为c,b,a。 我们知道entry 中next是指向下个节点的,线程2执行完后,即b=c.next,a=b.next。再来看线程1,线程1恢复后执行,在头部插入a时,a.next指向b,而因为线程2 已经将b.next指向a。Entry的next节点永不为空,发生了死循环。

总结

数组是固定长度,链表太长就需要扩充数组长度进行rehash减少链表长度。如果两个线程同时触发扩容,在移动节点时会导致一个链表中的2个节点相互引用,从而生成环链表。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值