HashMap面试专题,不看后悔
以前也零零碎碎发过一些HashMap的文章,这次简短总结一下有关HashMap的重要考点,这也是求职面试的经常问的,看完记得点个赞和在看哦~
1、Hash的概念
将任意长度的输入通过散列算法之后映射成固定长度的输出。
2、Hash冲突
当关键字集合很大时(key的数量很多的时候),关键字值不同的元素可能会映像到哈希表的同一地址上,即K1!=K2,但f(K1)=f(K2),这种现象称为hash冲突,实际中冲突是不可避免的,只能通过改进哈希函数的性能来减少冲突。
3、你认为好的Hash算法的点应该有哪些?
(1)效率得高,做到长文本也能高效计算出Hash值
(2)根据Hash值不能逆推出原文
(3)两次输入,如果有一点不同也得保证Hash值是不同的
(4)尽可能要分散,因为在table中slot大部分都处于空闲状态时要尽可能降低Hash冲突
4、HashMap的存储结构长啥样?
JDK1.8:
(1)数组+链表+红黑树构成,每个数据单元为一个Node结构,Node结构中有key字段、value字段、next字段、hash字段
(2)next字段就是发生Hash冲突的时候,当前桶位中的Node与冲突Node连接成一个链表所需要的字段JDK1.7:
数组+链表
45、如果创建HashMap的时候没有指定HashMap散列表的长度,初始长度为多少?
在JDK 8中,关于默认容量的定义为:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16 ,其故意把16写成1<<4,就是提醒开发者,这个地方要是2的幂。
(1)为啥用位运算呢?直接写16不好么?
这样是为了位运算的方便,位与运算比算数计算的效率高了很多,之所以选择16,是为了服务将Key映射到index的算法。
(2)那为啥用16不用别的呢?
因为在使用不是2的幂的数字的时候,Length-1的值是所有二进制位全为1,这种情况下,index的结果等同于HashCode后几位的值。这个值既不能太小,也不能太大。
太小了就有可能频繁发生扩容,影响效率,太大了又浪费空间,不划算。
只要输入的HashCode本身分布均匀,Hash算法的结果就是均匀的。
这是为了实现均匀分布。
6、散列表是New HashMap()的时候创建的,还是什么时候创建的?
散列表是懒加载机制,只有在第一次put数据的时候才创建(JDK1.8,JDK1.7是直接加载散列表)
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的扩容也不是简单的扩大点容量这么简单的。
8、扩容?它是怎么扩容的呢?
分为两步
(1)扩容:创建一个新的Entry空数组,长度是原数组的2倍。
(2)ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。
为什么要重新Hash呢,直接复制过去不香么?
是因为长度扩大以后,Hash的规则也随之改变。
比如原来长度(Length)是8你位运算出来的值是2 ,新的长度是16你位运算出来的值明显不一样了。
9、链表转化为红黑树的条件
(1)链表长度达到8
(2)当前散列表长度达到64
以上两个条件同时满足链表才会转化为红黑树,如果仅仅链表长度达到8,它不会发生链表转红黑树,只会发生一次散列表扩容(resize)
10、Node对象里面的hash字段的值是key对象的hashcode的返回值吗?
不是的,通过key的hashcode的高16位异或低16位得到的新值,这样即使数组table的length比较小的时候,也能保证高低bit都参与到Hash的计算中,避免高16位浪费没起到作用,尽可能的得到一个均匀分布的hash。
11、为啥我们重写equals方法的时候需要重写hashCode方法呢?你能用HashMap给我举个例子么?
因为在java中,所有的对象都是继承于Object类。Ojbect类中有两个方法equals、hashCode,这两个方法都是用来比较两个对象是否相等的。
在未重写equals方法我们是继承了object的equals方法,那里的 equals是比较两个对象的内存地址,显然我们new了2个对象内存地址肯定不一样
比如发生Hash冲突的时候,我们去get,他就是根据key去hash然后计算出index,找到了2,那我怎么找到具体的”电脑“还是”脑电“呢?
equals!是的,所以如果我们对equals方法进行了重写,建议一定要对hashCode方法重写,以保证相同的对象返回相同的hash值,不同的对象返回不同的hash值。
12、HashMap的put数据的流程
13、为什么java8以后链表数据超过8以后,就改成红黑树存储?
这就涉及到拒接服务攻击了,比如某些人通过找到你的hash碰撞值,来让你的HashMap不断地产生碰撞,那么相同key位置的链表就会不断增长,当你需要对这个HashMap的相应位置进行查询的时候,就会去循环遍历这个超级大的链表,性能及其地下。java8使用红黑树来替代超过8个节点数的链表后,查询方式性能得到了很好的提升,从原来的是O(n)到O(logn),容器中节点分布在hash桶中的频率遵循泊松分布,桶的长度超过8的概率非常非常小(约为10万分之一),所以作者应该是根据概率统计而选择了8作为阀值。
14、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)
15、首先HashMap是线程不安全的,其主要体现在哪里?
(1)在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。
(2)在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
本文分享自微信公众号 - Java技术大联盟(jingdakunye520)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Python数据可视化,seaborn如何做出非常规图表
公众号发送"可视化",获取源码与数据 前言 上一节我们单纯使用 matplotlib 制作出以下图表: 每年小麦产量柱状图 使用不同颜色标记最小与最大值的柱子 但是,如果只是制作标准的图表,我们有许多其他的选择。最常见的就是使用 seaborn ,他是基于 matplotlib 的包装。 这一节我们就来看看,如何使用 seaborn 生成标准图表,然后结合 matplotlib 做出定制效果。 特别是多系列的情况下,会有一些技巧。 本文目标图表是这样子: 2个系列。每个系列找出最小最大的柱子,标记成不同的颜色 本文所需要的库如下: 数据是这样子: 上一节做的事情如下: 设置 x 轴标签的旋转角度 设置某个指定柱状图的柱子颜色 简单把这些事情包装成函数: 使用 seaborn 的代码,实际与上一节直接使用 matplotlib 差不多: 看起来 seaborn 没有特别的地方! 这是因为我们只有一个系列(上图只涉及2个维度:wheat 与 year) 多系列 稍微修改一下数据, 行3、4、5:复制一份数据,小麦产量随机生成 行7、8:新增一个列"type",把数据划分成2类:"原始" 、...
- 下一篇
machinery入门看这一篇(异步任务队列)
前言 哈喽,大家好,我是asong,这次给大家介绍一个go的异步任务框架machinery。使用过python的同学们都知道Celery框架,machinery框架就类似于Celery框架。下面我们就来学习一下machinery的基本使用。 自己翻译一个粗略版的machinery中文文档,有需要的伙伴们公众号自取无水印版:后台回复:machinery即可领取。 或者github下载:https://github.com/asong2020/Golang_Dream/tree/master/machinery 抛砖引玉 我们在使用某些APP时,登陆系统后一般会收到一封邮件或者一个短信提示我们在某个时间某某地点登陆了。而邮件或短信都是在我们已经登陆后才收到,这里就是采用的异步机制。大家有没有想过这里为什么没有使用同步机制实现呢?我们来分析一下。假设我们现在采用同步的方式实现,用户在登录时,首先会去检验一下账号密码是否正确,验证通过后去给用户发送登陆提示信息,假如在这一步出错了,那么就会导致用户登陆失败,这样是大大影响用户的体验感的,一个登陆提示的优先级别并不是很高,所以我们完全可以采用异步...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果