HashMap面试专题,不看后悔

之前也零零碎碎发过一些HashMap的文章,此次简短总结一下有关HashMap的重要考点,这也是求职面试的常常问的,看完记得点个赞和在看哦~
java

一、Hash的概念 

任意长度的输入经过散列算法以后映射成固定长度的输出面试

二、Hash冲突 

当关键字集合很大时(key的数量不少的时候),关键字值不一样的元素可能会映像到哈希表的同一地址上,即K1!=K2,但f(K1)=f(K2),这种现象称为hash冲突,实际中冲突是不可避免的,只能经过改进哈希函数的性能来减小冲突算法

三、你认为好的Hash算法的点应该有哪些? 

(1)效率得高,作到长文本也能高效计算出Hash值

(2)根据Hash值不能逆推出原文

(3)两次输入,若是有一点不一样也得保证Hash值是不一样的

(4)尽量要分散,由于在table中slot大部分都处于空闲状态时要尽量下降Hash冲突数组

四、HashMap的存储结构长啥样?

JDK1.8:安全

(1)数组+链表+红黑树构成,每一个数据单元为一个Node结构,Node结构中有key字段、value字段、next字段、hash字段
(2)next字段就是发生Hash冲突的时候,当前桶位中的Node与冲突Node链接成一个链表所须要的字段

JDK1.7:微信

数组+链表多线程

4五、若是建立HashMap的时候没有指定HashMap散列表的长度,初始长度为多少?

在JDK 8中,关于默认容量的定义为:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16 ,其故意把16写成1<<4,就是提醒开发者,这个地方要是2的幂app

(1)为啥用位运算呢?直接写16很差么?
这样是为了位运算的方便,位与运算比算数计算的效率高了不少,之因此选择16,是为了服务将Key映射到index的算法。编辑器

(2)那为啥用16不用别的呢?
由于在使用不是2的幂的数字的时候,Length-1的值是全部二进制位全为1,这种状况下,index的结果等同于HashCode后几位的值。这个值既不能过小,也不能太大
过小了就有可能频繁发生扩容,影响效率,太大了又浪费空间,不划算。函数

只要输入的HashCode自己分布均匀,Hash算法的结果就是均匀的。

这是为了实现均匀分布

六、散列表是New HashMap()的时候建立的,仍是何时建立的?

散列表是懒加载机制,只有在第一次put数据的时候才建立(JDK1.8,JDK1.7是直接加载散列表

七、负载因子默认是多少,有啥做用?为何负载因子为0.75?何时进行扩容(resize)?

(1)默认为0.75,用于计算扩容阈值
(2)loadFactor是负载因子,表示HashMap满的程度,默认值为0.75f,设置成0.75有一个好处,那就是0.75正好是3/4而capacity又是2的幂。因此,两个数的乘积都是整数
(3)影响扩容主要有两个因素:
  Capacity:HashMap当前长度。
  LoadFactor:负载因子,默认值0.75f。
  怎么理解呢,就好比当前的容量大小为100,当你存进第76个的时候,判断发现大于扩容阈值100*0.75=75须要进行resize了,那就进行扩容,可是HashMap的扩容也不是简单的扩大点容量这么简单的。

八、扩容?它是怎么扩容的呢?

分为两步

(1)扩容:建立一个新的Entry空数组,长度是原数组的2倍

(2)ReHash:遍历原Entry数组,把全部的Entry从新Hash到新数组

为何要从新Hash呢,直接复制过去不香么?

是由于长度扩大之后,Hash的规则也随之改变。

好比原来长度(Length)是8你位运算出来的值是2 ,新的长度是16你位运算出来的值明显不同了。

九、链表转化为红黑树的条件

(1)链表长度达到8
(2)当前散列表长度达到64
以上两个条件同时知足链表才会转化为红黑树,若是仅仅链表长度达到8,它不会发生链表转红黑树,只会发生一次散列表扩容(resize)

十、Node对象里面的hash字段的值是key对象的hashcode的返回值吗?

不是的,经过key的hashcode的高16位异或低16位获得的新值,这样即便数组table的length比较小的时候,也能保证高低bit都参与到Hash的计算中,避免高16位浪费没起到做用,尽量的获得一个均匀分布的hash

十一、为啥咱们重写equals方法的时候须要重写hashCode方法呢?你能用HashMap给我举个例子么?

由于在java中,全部的对象都是继承于Object类。Ojbect类中有两个方法equalshashCode,这两个方法都是用来比较两个对象是否相等的。

在未重写equals方法咱们是继承了object的equals方法,那里的 equals是比较两个对象的内存地址,显然咱们new了2个对象内存地址确定不同

好比发生Hash冲突的时候,咱们去get,他就是根据key去hash而后计算出index,找到了2,那我怎么找到具体的”电脑“仍是”脑电“呢?

equals!是的,因此若是咱们对equals方法进行了重写,建议必定要对hashCode方法重写,以保证相同的对象返回相同的hash值,不一样的对象返回不一样的hash值

十二、HashMap的put数据的流程

1三、为何java8之后链表数据超过8之后,就改为红黑树存储?

这就涉及到拒接服务攻击了,好比某些人经过找到你的hash碰撞值,来让你的HashMap不断地产生碰撞,那么相同key位置的链表就会不断增加,当你须要对这个HashMap的相应位置进行查询的时候,就会去循环遍历这个超级大的链表,性能及其地下。java8使用红黑树来替代超过8个节点数的链表后,查询方式性能获得了很好的提高,从原来的是O(n)到O(logn),容器中节点分布在hash桶中的频率遵循泊松分布,桶的长度超过8的几率很是很是小(约为10万分之一),因此做者应该是根据几率统计而选择了8做为阀值。

1四、Hashmap的结构,1.7和1.8有哪些区别?

(1)JDK1.7用的是头插法,而JDK1.8及以后使用的都是尾插法,那么他们为何要这样作呢?
由于JDK1.7是用单链表进行的纵向延伸,当采用头插法时会容易出现逆序且环形链表死循环问题。可是在JDK1.8以后是由于加入了红黑树使用尾插法,可以避免出现逆序且链表死循环的问题。

(2)扩容后数据存储位置的计算方式也不同:1. 在JDK1.7的时候是直接用hash值和须要扩容的二进制数进行&(这里就是为何扩容的时候为啥必定必须是2的多少次幂的缘由所在,由于若是只有2的n次幂的状况时最后一位二进制数才必定是1,这样能最大程度减小hash碰撞)(hash值 & length-1)

1五、首先HashMap是线程不安全的,其主要体如今哪里?

(1)在jdk1.7中,在多线程环境下,扩容时会形成环形链或数据丢失

(2)在jdk1.8中,在多线程环境下,会发生数据覆盖的状况。


若是你喜欢本文,
请长按二维码,关注 Java技术大联盟.
转发至 朋友圈 ,是对我最大的支持。

点个 在看 
喜欢是一种感受
在看是一种支持

本文分享自微信公众号 - Java技术大联盟(jingdakunye520)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。