zoukankan      html  css  js  c++  java
  • 【面试】HashMap

    1、HashMap的继承关系

     2、基本属性

    • 非线程安全
    • 初始容量16,加载因子0.75,扩容为原来的2倍
    • key可以存null(hash值为0),但智能存一个,value可以存null,可以有多个
    • 1.8前数组+链表,1.8后数组+链表+红黑树,桶内的节点个数>8后执行treeifyBin方法,判断长度是否大于最小红黑树容量64,小于扩容,大于则进行树化
    • 扩容是2的倍数

    3、JDK8中的HashMap有哪些改动?

    • JDK7中的底层实现是数组+链表,JDK8中使用的是数组+链表+红黑树。
    • JDK7中扩容时有可能出现死锁,JDK8中通过算法优化不会出现死锁了。
    • JDK8中对算哈希值的哈希算法进行了简化以提高运算效率
    • 链表的插入方式由头插改成了尾插法

    4、JDK8中为什么要使用红黑树?

    数据量特别大的时候会产生hash碰撞,导致某个链表数据量特别大,链表的查找速度比较慢复杂度是O(n),影响比较差;因此优化成节点数量>8时转化成红黑树,查找的时间复杂度为O(logn)

    5、HashMap的put流程

    ①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;

    ②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③;

    ③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals;

    ④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤;

    ⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;

    ⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。

     2)另一种理解

    • 对 Key 求 Hash 值,然后再计算 下标。
    • 如果没有碰撞,直接放入桶中,
    • 如果碰撞了,以链表的方式链接到后面,
    • 如果链表长度超过阀值(TREEIFY_THRESHOLD == 8),就把链表转成红黑树。
    • 如果节点已经存在就替换旧值
    • 如果桶满了(容量 * 加载因子),就需要 resize。

    6、get流程

    7、扩容 

    8、为什么以2的倍数进行扩容?

     降低发生碰撞的概率,使散列更均匀。根据key的hash值计算bucket的下标位置时,为了得到这个索引值必须对扰动后的数跟数组长度进行取余运算,即 hash % n (n为hashmap的长度),又因为&比%运算快,n如果为2的倍数,就可以将%转换为&,结果就是 hash & (n-1),所以HashMap长度是2的倍数

     9、计算hash的时候为什么  ^(h>>>16)  ?

    10、HashMap线程安全方面会出现什么问题

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

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

    2)resize而引起死循环

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

    如果还不明白的话看这两篇文章就可以:

    11、Map的遍历方式

    参考:

    hashmap面试

    hasjmap源码分析(1.8) 

    重新认识hashmap

  • 相关阅读:
    一个java点菜程序
    团队成员
    CentOS tomcat普通用户执行配置安装
    varnish的监控和调试
    组播协议和组播路由
    AVAYA加外线DID,30B+D
    vi技巧
    CentOS开启Telnet服务
    juniper 虚拟路由
    DOS批处理下操作telnet实现自动远程登录操作
  • 原文地址:https://www.cnblogs.com/songjn/p/13992665.html
Copyright © 2011-2022 走看看