前言
HashMap作为面试中经常被问到的数据结构,而且我们平时用到的也非常多,那么今天就来盘一盘HashMap的源码。结合平时背的关于HashMap的八股文来看一看他底层到底的怎么实现的,这样以后再面对面试官的夺命连环问也能应付自如了不是。
本文是基于java8的HashMap分析。
构造函数
有4个构造函数,其中无参构造是给loadFactor赋一个默认值。这个loadFactor就是负载因子。默认0.75;
我们也可以自己指定负载因子数,但是我们指定的负载因子必须大于0。
注释上写的明明白白,这个initialCapacity是初始化的大小。
我们知道HashMap底层是数组+链表+红黑树(jdk8),以K,V的形式存在,那他刚创建的数组的大小就是可以通过initialCapacity这个参数来指定,默认是16。
那么问题来了。
面试官:他是在new HashMap的时候就给初始化数组大小的么?
我:额。。。是啊。
面试官:指定初始容量创建的时候是指定多少就是多少么?比如指定容量大小为17,那他创建的数组大小是17么?
我:额。。。不是么?
面试官:。。。。。
话不多说,上代码:
这里好像并没有数组什么事,只是把初始容量经过tableSizeFor后赋给了threshold,这个tableSizeFor方法等会再看。
那我们再看数组上面的注释。
在第一次put的时候才去初始化大小!!
而在指定初始容量大小的创建的时候只是把阈值大小给指定了。看代码:
我们再回头看这个给threshold赋值的方法:tableSizeFor这个方法:
根据注释,我们看出:他是获取目标值的最小二次幂。
那么这两个问题的答案就显而易见了,
首先,他是在第一次put的时候才去进行数组的初始化。
然后他初始化的大小是传入值的最小二次幂,比如指定是8 那么初始化大小为8。指定是15 初始化大小为16。
数组+链表+红黑树
HashMap经过1.8版本优化之后他是把链表的头插法改成的尾插法,链表到达一定条件后转为红黑数的数据结构。那我们来看下这两个步骤是在什么地方进行的吧。
我们再看这个转红黑树的方法:他是直接就把链表转成红黑树吗?
他是进行了一次判断:如果数组的长度大于64的时候才去转红黑树的,否则把原来的数据进行一次resize();
那么我们就得出结论:链表在长度大于8的时候,且数组的长度大于64的时候才会转为红黑树的数据结构。
经过我们一顿分析,可以画出HashMap的数据结构:
扩容
我们知道HashMap扩容是每次扩容为原来的两倍,那他是怎么进行扩容的呢?答案就再resize()方法里面
这时候面试官又问了:
面试官:扩容的时候旧数据进行rehash之后不是和原来的hash值一样吗,还是会造成hash冲突,HashMap是怎么保证resize之后数据均匀的分布呢?
你:。。。。
看代码:
圈起来的地方就是答案所在。把hash值和长度进行逻辑与处理后再放到新数组,这样就可以保证旧数据再新的数组里面均匀分布啦。
经过文本的分析,想必各位靓仔对HashMap的理解已经臻至化境。下次再遇到关于HashMap的面试题那不是随便拿捏。
最后说一句
才疏学浅,难免会有纰漏,如果文章有不对的地方欢迎大家指正,一起进步。
本文转载自: 掘金