hashMap 面试遇到的一些总结,欢迎大家来交流。

注:
看hashMap源码需要了解:二进制运算符号

1、位异或运算(^):二进制运算,如果相同则为0,不相同则为1
2、 位与运算符(&):二进制运算,如果两个数都为1则为1,否则为0
3、位或运算符(|):二进制运算,如果两个数有一个为1则为1,否则为0
4、位非运算符(~):如果位为0,结果是1,如果位为1,结果是0
5、 << 、>> 带符号移动
6、>>> 无符号右移:注意 没有无符号左移!

1、HashMap的默认容量?

阿里规约要求,创建Hashmap,需要指定默认容量。

2、如何计算hash值

获得Hash算法本质上大致分为三步:

1、获得key的hashcode
2、高位运算
3、取模运算

注:hashmap 的put和get 过程,都是计算得到hash值,然后确定hash桶的坐标。
1、获得key的hashcode,并高位计算
通过hashcode()方法,使其无符号右移 16 位,并且与自身 位异域运算

问题来了,为什么要先无符号右移 16位呢。

" >>> ":不管正负标志位为0还是1,将该数的二进制码整体右移,左边部分总是以0填充,右边部分舍弃。

1111 1111 1111 1111 1011 1011 1110 1100  (key的hashcode)>>> 16  (无符号右移16位)
———————————————————————————————————————
0000 0000 0000 0000 1111 1111 1111 1111

1.1、因为我们常使用的hashmap的容量不会大于 65636(2^16),所以 65636 用二进制表示 就是16位的二进制。
1.2、最后计算 hash桶的下标需要跟 hash 低位 ,做位与(&)运算。在做位与运算之前,要拿到均匀分布的hash值。根据key取hashcode,右移十六位,而且与自己做位异或(^)运算,就是尽量让自己变得更加散列。

2、取模运算
(n-1) &h 运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。
于是乎用该hash与map(总容量-1)做 位与运算, 得到一个 小于haspMap总容量的一个整数。

文字叙述:
如果 put一个 (“key”,“value”),首先根据 key 调用 hashcode()方法生成 hash值,然后无符号右移 16 位(jdk7 没有这一部,jdk8为了是hash 低16位分布更加均匀,因为一般的长度不会超过 2的16次方)。
然后进行 位异域运算 ,之后再与 容器长度(length -1)进行 位与运算,得到一个hash桶下标。

3、如何解决hash冲突

hash冲突定义:

不同的key,通过一系列hash计算可能会得到,相同的hash桶下标。
hashmap采用链地址法解决hash冲突

1、如果初次插入,判断 tab数组节点 是否为空,如果为空 会 new一个node节点

2、如果通过一系列二进制运算得到 的hash值,在原本的数组节点已经存在。
这里分为俩种情况。
一种是 map.put(“a”,“旧值”) ,再次 map.put(“a”,"新值 ") ,相同key 覆盖的情况。
会比较俩者hash 值,并且比较二者的key 是不是相同,
如果二者经过一系列二进制运算得到的hash值相同,
并且key(这里的key指的是这里的 “a”)也相同,就会重写写入该值。
另外一种就是 key 不同,就会发生冲突。

3、发生hash冲突,写入链表。
基于步骤2,满足hash值相同,并且 key值不相同。
会使用指针 p.next 指向并新创建一个 node节点保存hash冲突的key和value。

4、如果发生冲突的时候,会检测我们桶节点,链表的长度。如果链表的长度大于8,会转成红黑树。

为什么链表长度等于 8 转成红黑树。

其实转成红黑树需要俩个条件:

1、先满足链表的长>8

binCount 默认是0,当 binCount =7的时候,链表已经有8个元素了,
但是树化之前,已经执行了 p.next = newNode(hash, key, value, null);
所以此时有8+1=9个元素

2、再满足hash桶数组的长度 >=64,如果不满足会去扩容

可以回答
正常来说,链表长度等于 6 的时候,使用红黑树已经比链表效率高了。

#红黑树查询复杂度为:log(2n),也简称为log(n)
log 2n =6  =>  n = 2.580...
#单链表查询复杂度为: O(n)
2n =6  =>  n = 3
#由此可见。链表等于6时,红黑树已经高于链表查询复杂度。

但是根据泊松分布(源码注释)表示,若当节点数量等于6的概率还是很大的,而当大于8的概率就是百万分之一,已经很小了,hash冲突时为了避免在红黑树和链表之前频繁转换,所以定为8。

4、为什么hashMap 的数组大小为什么一定是 2 的幂?

1、HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法;

2、只有它的长度是2的N次方,对它进行减一操作,才能拿到所有是 1 的值
,这样对它进行 按位与运算时,才能快速的用位运算的方式,拿到数组的下标,并且 保证下标在容量之下,并且分配均匀
eg:
假设我们 创建一个默认容量为32的map ,如果经过 map.put(“aaa”,“我是value”) 的操作,
首先得到一个key=“aaa” 的hash值 (11100001 … 11011),去跟 (32-1)做 位于运算。

11100001 .... 11011 (key的hashcode)& 11111 (31的二进制)
——————————————————— = 11011 (32与31 进行位与运算(&)二进制)= 27    (十进制)
#能保证最后的结果在 (0-32之中)

5、为什么hashmap负载因子是0.75

源码上面注释大致意思就是说负载因子是0.75的时候,空间利用率比较高,
而且避免了相当多的Hash冲突,使得底层的链表或者是红黑树的高度比较低,提升了空间效率。
如果太高会导致查询复杂度增加,如果太低会增加存储空间

根据统计学来说。使用随机哈希码,节点出现的频率在hash桶遵循泊松分布。
在负载因子0.75下,每个碰撞位置的链表长度超过8个概率很低,而出现 6或者 7个还挺大的,这里直接降低了,每个hash桶底层的查询复杂度

6、JAVA7 HashMap的问题

1、并发环境容易死锁
2、可以通过精心构造的恶意请求引发Dos

7、java7到java8 做了哪些改进?为什么?

1.7和1.8主要在处理哈希冲突和扩容问题上区别比较大。

1、底层设计改变

JDK1.8 (数组+ 单链表 + 红黑树 )解决了1.7的大数据 查询效率问题
JDK1.7的时候使用的是(数组+ 单链表的数据结构)。但是在JDK1.8及之后时,使用的是数组+链表+红黑树的数据结构(当链表的深度达到8的时候,也就是默认阈值,就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从O(n)变成O(logN)提高了效率)出现哈希冲突时,1.7把数据存放在链表,1.8是先放在链表,链表长度超过8就转成红黑树

2、扩容设计改变

扩容时插入顺序的改变,解决了1.7的扩容时发生死锁的问题
区别:
JDK1.7用的是头插法,有可能在扩容时,出现回环,造成死锁
而JDK1.8及之后使用的都是尾插法,但是仍有线程安全问题
总结:
HashMap之所以在并发下的扩容造成死循环,是因为,多个线程并发进行时,因为一个线程先期完成了扩容,将原的链表重新散列到自己的表中,并且链表变成了倒序,后一个线程再扩容时,又进行自己的散列,再次将倒序链表变为正序链表。于是形成了一个环形链表,当表中不存在的元素时,造成死循环。

虽然在JDK1.8中,Java的开发小组修正了这个问题,但这个问题并不是bug,只能说开发者使用不当造成的,但是HashMap始终存在着其他的线程安全问题。所以在并发情况下,我们应该使用HastTable或者ConcurrentHashMap来代替HashMap。

8、为什么说,hashmap 不是线程安全的

1、HashMap 在插入的时候
  现在假如 A 线程和 B 线程同时进行插入操作,然后计算出了相同的哈希值对应了相同的数组位置,因为此时该位置还没数据,然后对同一个数组位置,两个线程会同时得到现在的头结点,然后 A 写入新的头结点之后,B 也写入新的头结点,那B的写入操作就会覆盖 A 的写入操作造成 A 的写入操作丢失。
2、HashMap 在扩容的时候
  HashMap 有个扩容的操作,这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。
  那么问题来了,当多个线程同时进来,检测到总数量超过门限值的时候就会同时调用 resize 操作,各自生成新的数组并 rehash 后赋给该 map 底层的数组,结果最终只有最后一个线程生成的新数组被赋给该 map 底层,其他线程的均会丢失。
3、HashMap 在删除数据的时候
  删除这一块可能会出现两种线程安全问题,第一种是一个线程判断得到了指定的数组位置i并进入了循环,此时,另一个线程也在同样的位置已经删掉了i位置的那个数据了,然后第一个线程那边就没了。但是删除的话,没了倒问题不大。
  其他地方还有很多可能会出现线程安全问题,我就不一一列举了,总之 HashMap 是非线程安全的,有并发问题时,建议使用 ConcrrentHashMap。

9、haspMap 为什么使用红黑树

hashMap 的场景要求,查询快,插入快

1、红黑树(不完美平衡红黑树)
特点: 不追求完全平衡
插入比较快,因为不需要过多的自旋操作来维持,节点的绝对平衡。

2、avl树 (完美平衡树)
特点:
查询比较快,底层数据插入比较慢,为了维持高度的平衡,就要付出更多代价。

区别
查询复杂度:
        红黑树和avl树 查找的话都是logn
插入、删除,复杂度:
       平衡树一般是 logn,可能需要通过一次或多次树旋转来重新平衡这个树红黑树一般是 也是logn。但不需要额外的自旋。
此外由于它的设计,任何不平衡都会在三次旋转之内解决。

10、HashMap中的modcount表示什么什么意思?

modcount:修改次数

在集合【ArrayList,LinkedList,HashMap】等的内部实现增,删,改中,都有涉及到 modcount。
为什么要有修改 modcount?
其实这些涉及到modcount的集合都有共同的特点就是,都不是线程安全的。
HashMap也不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是通过 modCount 实现的。

11、HashMap为什么会出现ConcurrentModificationException?

【ArrayList,LinkedList,HashMap】等使用forEach删除时,会报错ConcurrentModificationException,因为在forEach遍历时,是不允许map元素进行删除和增加。
使用iterator迭代删除时没有问题的,在每一次迭代时都会调用hasNext()方法判断是否有下一个,是允许集合中数据增加和减少。

HashMap面试灵魂几问相关推荐

  1. TCP协议面试灵魂10问 | 强势整理

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群",加入新技术 来源 | urlify.cn/rqumIn 先亮出这篇文章的思维导 ...

  2. TCP 协议面试灵魂 12 问 | 强势整理

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | urlify.cn/rqumIn 先亮出这篇文 ...

  3. 全网第一 | Flink学习面试灵魂40问答案,文末有福利!

    大数据技术与架构 点击右侧关注,大数据开发领域最强公众号! 暴走大数据 点击右侧关注,暴走大数据! 来源:王知无 作者:王知无 By 暴走大数据 场景描述:这是一份Flink学习面试指北.看看你搞清楚 ...

  4. JAVA面试灵魂108问(二十六)---JVM2

      大家好,我是陈哈哈,北漂五年.相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题的形式,带你过一遍 ...

  5. TCP 协议面试灵魂10问,建议收藏~

    Proc 先亮出这篇文章的思维导图 TCP 作为传输层的协议,是一个软件工程师素养的体现,也是面试中经常被问到的知识点.在此,我将 TCP 核心的一些问题梳理了一下,希望能帮到各位. 001. 能不能 ...

  6. 易语言tcp多线程服务端客户端_太详细了,TCP协议面试灵魂10问,建议收藏!

    推荐阅读: 阿里P9架构师120分钟带你掌握线程池,不在为线程而烦恼​www.bilibili.com 不懂算法怎么去字节等大厂面试?左程云大神联合马士兵大佬120分钟带你掌握算法底层​www.bil ...

  7. JAVA面试灵魂108问(三十六)---实战那些事儿2

      大家好,我是陈哈哈,北漂五年.相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题的形式,带你过一遍 ...

  8. TCP 协议面试灵魂 12 问(二)

    007: 能不能说说TCP报文中时间戳的作用? timestamp是 TCP 报文首部的一个可选项,一共占 10 个字节,格式如下: kind(1 字节) + length(1 字节) + info( ...

  9. 2020,Laya最新中高级面试灵魂32问,你都知道吗?

    ##前言 上个月陆续给大家更新了cocos和unity的面试题,相信读者朋友们看了之后多多少少也有了一些收获,在之前的分享中,有部分读者粉丝一直私信阿博,让我分享一些金九银十服务器和Laya的面试题, ...

  10. 大数据/数仓面试灵魂30问

    1.什么是数据仓库?如何构建数据仓库?(如果这个问题回答的好,后面很多问题都不需要再问) 2.如何建设数据中台?可简单说下理解与思路 3.数据仓库.数据中台.数据湖的理解 4.传统数仓的程度(建模工具 ...

最新文章

  1. g++报错解决:error: ‘setw’ is not a member of ‘std’
  2. eclipse 右键项目为什么没有properties菜单_只需几步,从零开始搭建SSM项目
  3. RxJava使用(二)filter 操作符
  4. 基于 eBPF 的 prometheus 监控方案
  5. php 函数传值_传址_函数参数,php函数的传值与传址(引用)详解
  6. 680. Valid Palindrome II
  7. openstack-networking-neutron(一)---端到端和点到点的理解
  8. Android开发之虹软人脸识别活体检测基本步骤
  9. 关于PHPExcel 导出下载表格,调试器响应乱码
  10. 多路查找树之2-3树的删除原理 - 数据结构和算法81
  11. javascript 中的location.pathname
  12. Android怎么禁用底部键,在三分钟内完全禁用并隐藏Android设备底部的虚拟按钮(适用于测试)...
  13. 渗透测试服务 网站渗透真的难吗?
  14. blp模型 上读下写_谁能简单解释一下经济学中的BLP模型?
  15. java 配置全局过滤器,如何为Spring Cloud Gateway加上全局过滤器
  16. Alpha测试与Beta测试及区别
  17. C++数据结构和算法2 栈 双端/队列 冒泡选择插入归并快排 二三分查找 二叉树 二叉搜索树 贪婪 分治 动态规划
  18. 自由度和剧情新颖的单机游戏
  19. C#treeview树形菜单
  20. Linux中的默认权限与隐藏权限(文件、目录)

热门文章

  1. 修改战网昵称服务器错误,暴雪又改了游戏平台名字 暴雪战网回来了
  2. java pdf添加页码_Java 给PDF文档添加页码
  3. java nas_NAS对家庭来说有什么用处?
  4. android 实现果冻动画效果,Android果冻效果(阻尼动画)
  5. Outlook显示ost has reached maximum size
  6. 【比赛游记】NOI2019打铁记
  7. 小白学习Java第七天
  8. CentOS停更;阿里发布全新操作系统(Anolis OS),用后直呼:牛X
  9. Hive 动态分区恢复静态分区表数据
  10. VMware 8安装Mac OS X 10.7 Lion