大家好,我是陶然同学,软件工程大三明年实习。认识我的朋友们知道,我是科班出身,学的还行,但是对面试掌握不够,所以我将用这100多天更新Java面试题🙃🙃。
不敢苟同,相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题的形式,带你过一遍热门面试题及恰如其分的解答。当然,我不会太深入,因为我怕记不住!!
因此,不足的地方希望各位在评论区补充疑惑、见解以及面试中遇到的奇葩问法,希望这100天能够让我们有质的飞越,一起冲进大厂!!,让我们一起学(juan)起来!!!
谈谈Concurrent(可考润s)HashMap的扩容机制
1.7版本
1.
1.7版本的ConcurrentHashMap是基于Segment(色们)分段实现的
2.
每个Segment相对于⼀个⼩型的HashMap
3.
每个Segment内部会进⾏扩容,和HashMap的扩容逻辑类似
4.
先⽣成新的数组,然后转移元素到新数组中
5.
扩容的判断也是每个Segment内部单独判断的,判断是否超过阈值
1.8版本
1.
1.8版本的ConcurrentHashMap不再基于Segment实现
2.
当某个线程进⾏put时,如果发现ConcurrentHashMap正在进⾏扩容那么该线程⼀起进⾏扩容
3.
如果某个线程put时,发现没有正在进⾏扩容,则将key-value添加到ConcurrentHashMap中,然
后判断是否超过阈值,超过了则进⾏扩容
4.
ConcurrentHashMap是⽀持多个线程同时扩容的
5.
扩容之前也先⽣成⼀个新的数组
6.
在转移元素时,先将原数组分组,将每组分给不同的线程来进⾏元素的转移,每个线程负责⼀组
或多组的元素转移⼯作
Jdk1.7到Jdk1.8 HashMap 发⽣了什么变化(底层)?
1.
1.7中底层是数组+链表,1.8中底层是数组+链表+红⿊树,加红⿊树的⽬的是提⾼HashMap插⼊
和查询整体效率
2.
1.7中链表插⼊使⽤的是头插法,1.8中链表插⼊使⽤的是尾插法,因为1.8中插⼊key和value时
需要判断链表元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使⽤尾插法
3.
1.7中哈希算法⽐较复杂,存在各种右移与异或运算,1.8中进⾏了简化,因为复杂的哈希算法的
⽬的就是提⾼散列性,来提供HashMap的整体效率,⽽1.8中新增了红⿊树,所以可以适当的简化
哈希算法,节省CPU资源
说⼀下HashMap的Put⽅法
先说HashMap的Put⽅法的⼤体流程:
1.
根据Key通过哈希算法与与运算得出数组下标
2.
如果数组下标位置元素为空,则将key和value封装为Entry对象(
JDK1.7中是Entry对象,JDK1.8中是Node对象)并放⼊该位置
3.
如果数组下标位置元素不为空,则要分情况讨论
a.
如果是JDK1.7,则先判断是否需要扩容,如果要扩容就进⾏扩容,如果不⽤扩容就⽣成Entry
对象,并使⽤头插法添加到当前位置的链表中
b.
如果是JDK1.8,则会先判断当前位置上的Node的类型,看是红⿊树Node,还是链表Node
ⅰ.
如果是红⿊树Node,则将key和value封装为⼀个红⿊树节点并添加到红⿊树中去,在这个
过程中会判断红⿊树中是否存在当前key,如果存在则更新value
ⅱ.
如果此位置上的Node对象是链表节点,则将key和value封装为⼀个链表Node并通过尾插
法插⼊到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会
判断是否存在当前key,如果存在则更新value,当遍历完链表后,将新链表Node插⼊到链
表中,插⼊到链表后,会看当前链表的节点个数,如果⼤于等于8,那么则会将该链表转
成红⿊树
ⅲ.
将key和value封装为Node插⼊到链表或红⿊树中后,再判断是否需要进⾏扩容,如果需要
就扩容,如果不需要就结束PUT⽅法