面试准备 --- HashMap

为了春招,系统地复习一下Java相关的知识,应该不会写源码解析,完全是给自己看的,如果足够幸运,有幸进入大厂,重新写下给别人看的

注意点

  • HashMap底层是数组和链表的结构,也就是数组中的元素是链表,在同一个链表中的值,都是键通过hash后相同的键值对.其实就是哈希表实现方法中的拉链法.
  • 下面说的对键求hash值都是值对键的hashcode值进行运算.
  • 先说一下一些默认值:
    • 默认初始容量是16,也就是说底层数组的长度
    • 负载因子,因为HashMap是通过hash定位的,所以不能和ArrayList一样,等底层数组填满了以后才进行扩容,所以负载因子的意思就是,当键值对数量超过负载因子乘以现在的容量,就进行扩容.同时,负载因子也代表了散列表的装填程度. 默认值是0.75,一般不用修改.
    • 扩容的大小是2倍
  • 说一个我觉得很好的设计,就是HashMap的底层数组的长度总是2的n次方,这里面还是有点说法的
    • 首先我想说一下操作系统内存管理中的页表设计,页面的大小也是2的n次方,它的设计理念是计算机可以根据逻辑地址直接看出页号和页内偏移地址.
    • HashMap的底层数组之所以是2的n次方,主要是因为hash,HashMap的hash算法是一个纯数学计算,但是根据键的hash值定位的方法却很有意思,是这样的h&(length - 1),h就是计算出的hash值,length就是底层数组的长度.其实现在就很容易看出了,如果length是2的n次方,其2进制形式就是一个1后面n个0,但是减一后,就是一个0,后面n个1,这样有什么好处,这个时候和h做&运算,就相当与取模运算,这样数组的hash分布就很均匀,这就是这样设计的原因.
  • 添加和删除节点操作没什么好说的,根据键算出hash值,然后找到数组中的位置,然后对链表进行操作即可,但这里有一点需要说的就是JDK1.8以前使用的是链表,但是到了1.8使用的就是一个红黑树,主要是当链表长度太长之后,相应的添加删除操作性能都不好,所以使用红黑树代替了链表.
  • hashMap中的并发问题,
    • hashMap的并发问题是经常被问到的一个问题,但我实在懒得截图解析,暂且干扯
    • 有两个问题,第一个是数据不一致的情况,这个很好理解.
    • 主要是第二个,也就是死循环的问题,为什么会产生死循环呢,这个如果要一步一步的理解,没有一张图还真的很难理解,主要是在put方法内部,对于添加新元素到一个链表中时,找到了位置,然后链表插入操作没有执行完时,这个时候其他线程触发扩容操作,所有链表重新计算位置,这时,之前新增的键值对如果换了位置,就可能在两个位置的链表间形成环路,一旦环路形成,再在此链表中进行操作,就会进入死循环.说是这么说了,但是具体的形成环的条件还需要具体按图分析.
    • 死循环的问题在1.8中已经解决了,上面也说了1.8使用的是红黑树,但是并发引起的数据不一致还是没有解决,这就需要我们的concurrentHashMap了,这个会另开一章说.

猜你喜欢

转载自blog.csdn.net/qq_36865108/article/details/86634713