今日内容
说起集合,我们面试中问的比较多的问题除了List和Set,也就是Map相关的问题了。而Map中问到最多的也就HashMap了,看过HashMap源码的人都知道,源码里面确实有很多东西可以学习和研究,所以我们今天主要是来总结hashmap相关的问题~
面试问题总结
- HashMap的底层数据结构
- JDK1.7之前,HashMap的底层数据结构是数组+链表
- JDK1.8以后,HashMap的底层数据结构就是数组+链表+红黑树
- HashMap底层为什么要用红黑树?
- 回答:红黑树能够避免拉链过长,从而影响hashMap的性能
- 分析:如果我们只是用数组+链表的结构,突然有一天数据量变大了,链表上的数据也就会随之变多,但是我们知道链表的查询效率比较低,很大程度上就会影响HashMap的性能,所以在JDK1.8以后就加入了红黑树。
- HashMap的处理逻辑
- HashMap的put实现:首先会对put进来的数据Key部分进行Hash值的比较,如果Hash值没有冲突,那么就直接放进数组,如果Key部分有冲突,那么就使用equals()方法进行比较,如果不同,那么就以链表的形式放在当前元素的后面,如果相同,那么新的节点就会代替老的节点,最后如果冲突后导致链表过长,导致链表长度超过8时,就会将其转换成红黑树。具体的可以看上篇文章中的问题5哈~
- HashMap的get实现:如果是数组中的第一个节点,那么直接命中啦,如果Hash有冲突,那么可以通过Key的equals()方法去寻找相对应的entry,查的时候可能是树也可能是链表,如果是树的时候效率会更高一写~
- HashMap的键值可以为Null吗?
- 回答:HashMap可以为Null,但是HashTable不能为Null
- 分析:通过查看源码我们可以知道,HashMap在计算Key的Hash值时,只是把Key作为形参传入hash()方法中,如果为Null,则返回0;而HashTable计算Key的Hash值的时候,是直接调用key的HashCode()方法,如果Key为Null,就会直接报出NullPointerException异常。
- HashMap线程安全吗?如果不安全那又如何解决呢?
- HashMap不是线程安全的!
- 解决方式:
- 用Hashtable替换HashMap。
- 用线程安全的ConcurrentHashMap替换HashMap。
- 用Collections工具类的synchronizedMap()方法,可以将不安全的map转化成一个线程安全的map,并且返回回来。
- HashMap的线程安全会发生在哪些阶段?
- 回答:
- 在JDK1.7中,扩容resize()操作时会造成循环链;
- 在JDK1.8中,在多个线程进行put操作时会发生数据覆盖的问题
- 分析:
- JDK1.7时,如果两个线程同时进行扩容,第一个线程扩容进行到一半时,第二个线程被调度,且完成扩容操作,第一个线程就会继续进行扩容,但是此时扩容的链表索引和刚开始扩容时的索引指向可能相反,从而也就会导致循环链!导致线程安全问题!
- JDK1.8时,如果有两个线程同时进行put操作,且他们的Key是一致的,此时很有可能会导致,第二个线程覆盖了第一个线程的值,从而导致线程安全问题!
- HashMap是如何做扩容的?
- 回答:每次以2的非0次幂的扩展方式去扩展,目的也就是为了减少Hash碰撞
- 分析:如果我们每次不是按照2的二次幂的方式去扩展,我们可以用Hash值与数组最大索引值进行与运算,最后计算出的索引位置碰撞的可能性会增大,可以自行算一下子,通过查看源码还能知道HashMap中的数组默认长度是16,加载因子为0.75。
- LinkedHashMap的特征有哪些?
- LinkedHashMap的底层实现其实就是HashMap,底层数据结构同样也是数组+链表+红黑树,但是和HashMap不同的是LinkedHashMap使用的是双向链表,可以保持键值对的插入顺序!
- 扩展知识点:
- 数组:是采用一段连续的存储单元来存储数据,查询的效率较高,多用于查询操作
- 链表:物存储单位上非连续、非顺序的储存结构,增删效率较高,多用于增删操作
- 红黑树:平衡二叉树,通过确保每条从根到叶子路径上各个节点的颜色来控制没有一条路径会比其他路径长两倍,因此近似平衡。用于搜索时,增加删除次数较多的情况!且根节点为黑色,节点需要为红色或者黑色,每个红色节点的子节点为黑色,通过变色和旋转进行自平衡的!
- B树:二叉树,每个节点只存储一个关键字,等于命中,小于走左节点,大于走右节点
- B+树:为叶子节点增加了链表指针,所有关键字都在叶子节点出现,单一节点能比B树存储更多的元素!
- 红黑树、B树、B+树等比较和使用场景会在后续陆续写出,大家记得看哈~
阶段性总结
通过这一段时间的学习,我们将List、Set和Map的一些常见问题进行了总结,在集合这一块,如果大家是校招出去面试的时候可能会问的比较多,所以大家可以参考我上面提出的这些问题和分析回答,结合自己的理解再做一个完整的总结,这样才会非常稳咩~
其实总结的时候也能学到很多东西,就比如看HashMap的源码,可以增强自己看源码的能力,以及单点调试的能力,以后如果去看Tomcat、Spring的源码等等,你就不会无从下手了,所以还是要静下心来,好好研究,可能真的会有意想不到的收获呦!
加油!!!