面试-1.HashMap问题

HashMap问题

  1. HashMap的默认初始容量是16.如果你使用构造方法时,传入的不是2的幂,会转化成大于这个数的2的幂
  2. HashMap的默认初始负载因子是0.75.
  3. hashmap扩容分为两步:
    扩容:创建一个新的Entry空数组,长度是原数组的2倍。
    ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。
  4. HahMap1.7为头插法,1.8改进为尾插法。

    1)Hashmap在插入元素过多的时候需要进行Resize,Resize的条件是 HashMap.Size >= Capacity * LoadFactor。2)Hashmap的Resize包含扩容和ReHash两个步骤,ReHash在并发的情况下可能会形成链表环。3)当调用get方法,进入此链表时,会进入死循环。
    链表头插法的会颠倒原来一个散列桶里面链表的顺序。在并发的时候原来的顺序被另外一个线程a颠倒了,而被挂起线程b恢复后拿扩容前的节点和顺序继续完成第一次循环后,又遵循a线程扩容后的链表顺序重新排列链表中的顺序,最终形成了环。
    

  1. 当链表长度大 于TREEIFY THRESHOLD (默认为8)时,将链表转换为红黑树,当然小于
    UNTREEIFY THRESHOLD (默认为6)时,又会转回链表以达到性能均衡。
  2. HashMap为什么是线程不安全的?

    HashMap在并发时可能出现的问题主要是两方面:

  • put的时候导致的多线程数据不一致

比如有两个线程A和B,首先A希望插入一个key value对到HashMap中,首先计算记录所要落到的hash桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A-样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的hash桶索弓和线程B要插入的记录计算出来的hash桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。

  • resize而引|起死循环

这种情况发生在HashMap自动扩容时,当2个线程同时检测到元素个数超过数组大小x负载因子。此时2个线程会在put()方法中调用了resize(),两个线程同时修改一个链表结构会产 生一个循环链表JDK1.7中, 会出现resize前后元素顺序倒置的情况)。接下来再想通过get(获取某一个元素,就会出现死循环。

猜你喜欢

转载自blog.csdn.net/TP89757/article/details/106985166