Java HashMap面试须知
前言
HashMap作为Java面试中高频出现的面试题,是面试官们最喜欢问的问题之一,通常会出现在前3道技术面试题中,主要是为了筛选不会Java的候选人,亦或者是考察候选人平时会不会看JDK源码,下面我们将从不同维度讲解在面试过程中,我们需要如何回答HashMap有关的面试题,以及对于面试而言,需要掌握到什么程度。
本文只是列举了一些常见且典型,并且需要花时间读源码的面试问题,并没有包括一些显而易见的简单的题,这样能让正在准备面试的你更快更全面的复习。
问题1:HashMap的实现原理
讲到HashMap的原理,大多数程序员都知道HashMap是基于数组加链表,这里我们简单讲一下原理。
我们先介绍一个HashMap内部的数据结构Node,结构很简单,hash字段存的是经过HashMap的hash()
方法得出的key
的hash值,key
和value
字段分别存的是键值对,next
字段存的是下一个Node对象.
static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;// ...
}
我们知道HashMap内部会维护一个Node类型的数组tab,当我们用put(key, value)来存储一个键值对时候,首先会用HashMap内置的hash方法得到这个key的hashcode,然后用这个hash值和数组的长度作逻辑与运算,结果就是该键值对需要存放的数组位置,如下。
// 检查tab[pos]是否已经有node元素了
pos = (tab.length -1) & hash(key)
如果tab[pos]已经有node元素了,说明发生了哈希冲突,那么Node中的next字段就会发挥作用,将哈希值相同的元素连起来变成链表。从这里可以看出Java中的HashMap解决冲突是使用拉链法。常见的解决哈希冲突的三种方法
- 开放寻址法:当发生哈希冲突时,按照一定次序从哈希表中寻找一个空闲的单元
- 拉链法:当发生哈希冲突时,将发生冲突的元素会以链表的方式存储
- 再哈希法:当发生哈希冲突时,会再哈希一次
问题2:HashMap的初始大小是多少
在不指定参数的情况下,初始大小会在第一次插入新元素时指定,Node数组大小为16,扩容阈值是总容量*0.75,初始时是12。
问题3:什么时候需要扩容,每次扩容多少
当发生下面2种情况是将会扩容,每次扩容到原数组大小的2倍
- 每次发生插入新元素操作时,都会检查HashMap的大小是不是超过了总容量*0.75
- 当需要把链表转成红黑树时,会先检查总的node数组长度是否小于64,如果是,会先进行扩容而不是转红黑树
问题4:链表什么时候会转成红黑树,转成红黑树有什么好处
当链表的长度第一次大于等于8时,且总的node数组长度大于64时会将链表转成红黑树。当我们扩容后,如果此时红黑树小于等于6个节点,则会转回链表。
我们都知道链表的查询时间复杂度是O(1),而红黑树是O(logN),所以当哈希冲突非常严重时,红黑树的效率要明显好于链表,当然在冲突不严重的时候,比如HashMap中定义的小于8时,链表的空间复杂度要明显优于红黑树,下面的Java Doc也做了相关的解释。
那么为什么会在大于等于8的时候需要转成红黑树呢,Java Doc中给出了这样一段解释:
/**
* Because TreeNodes are about twice the size of regular nodes, we
* use them only when bins contain enough nodes to warrant use
* (see TREEIFY_THRESHOLD). And when they become too small (due to
* removal or resizing) they are converted back to plain bins. In
* usages with well-distributed user hashCodes, tree bins are
* rarely used. Ideally, under random hashCodes, the frequency of
* nodes in bins follows a Poisson distribution
* (http://en.wikipedia.org/wiki/Poisson_distribution) with a
* parameter of about 0.5 on average for the default resizing
* threshold of 0.75, although with a large variance because of
* resizing granularity. Ignoring variance, the expected
* occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
* factorial(k)). The first values are:
*
* 0: 0.60653066
* 1: 0.30326533
* 2: 0.07581633
* 3: 0.01263606
* 4: 0.00157952
* 5: 0.00015795
* 6: 0.00001316
* 7: 0.00000094
* 8: 0.00000006
* more: less than 1 in ten million
**/
JDK解释说用红黑树所占用的空间几乎是链表的2倍,所以我们只在有很多哈希冲突的极端的情况下,那为什么当冲突数达到8时会被认为是极端情况呢,Java Doc解释到大多数的hash算法都是均匀分布的,会使用红黑树的场景非常少见。而理论上,一个随机分布的hash算法,元素在某个数组位置出现的概率是服从泊松分布,那么在0.75的扩容因子下,泊松分布的概率参数为0.5,此时发生8个相同hash值的概率为低于一千万分之一,所以一个设计良好的hash算法几乎是不会发生红黑树转换问题的。
问题5:HashMap中的Key是如何hash的
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
从这段code中可以发现一个有意思的逻辑,即让key的hashCode()
方法返回的hash值右移16位后再与hash作逻辑运算,我们知道hashCode()产生的是一个int值,而int值是32位的,当int值右移16位后,其实就是这个hashCode的高位和低位做了异或运算,为什么要这样呢,我们来看一下官方的Java Doc是怎么说的:
/*** Computes key.hashCode() and spreads (XORs) higher bits of hash* to lower. Because the table uses power-of-two masking, sets of* hashes that vary only in bits above the current mask will* always collide. (Among known examples are sets of Float keys* holding consecutive whole numbers in small tables.) So we* apply a transform that spreads the impact of higher bits* downward. There is a tradeoff between speed, utility, and* quality of bit-spreading. Because many common sets of hashes* are already reasonably distributed (so don't benefit from* spreading), and because we use trees to handle large sets of* collisions in bins, we just XOR some shifted bits in the* cheapest possible way to reduce systematic lossage, as well as* to incorporate impact of the highest bits that would otherwise* never be used in index calculations because of table bounds.
*/
翻译过来就是说,如果不同key的hashcode只在高位有差异的话,那么他们将会发生哈希冲突,为什么会这样呢,因为大多数情况下我们的HashMap的容量都不会非常大,而hash后的值是需要与HashMap的Node数组长度作逻辑与操作的,如果HashMap的数组长度很小,那么每次逻辑与操作都会丢失hashCode的高位值,从而导致哈希冲突。而高低位的异或运算是一种权衡之计,即解决了hash分布问题,又不太影响性能,在极端情况下还有红黑树来解决冲突。
问题6:为什么HashMap总是以2次幂来进行扩容
pos = (tab.length -1) & hash(key)
因为我们决定放在数组的哪个位置是使用上面的逻辑与的方法,tab.length是2次幂转成二进制就是1后面n个0,tab.length-1就是n个1,那么在做完逻辑与后,不同hash值得key所在的位置就是hash值本身所代表的位置,不会因为这个逻辑与而导致不同的hash的key在相同的位置。
而扩容之后,由于tab.length只是从n个1变成了n+1个1,那么原来hash值在n+1位前都相同的key还会在同一位置,而n+1位不同的key将会移到原来的2倍位置。
问题7:HashMap的线程安全版本有哪些,各自的使用场景
- Collections.synchronizedmap:通过将所有线程不安全的方法加上
synchronized
关键字实现,当我们只是需要数据一致性时可以使用这个实现 - ConcurrentHashMap:HashMap的线程安全版本,支持并发读,所以当我们需要频繁读写操作时,ConcurrentHashMap的性能优于Collections.synchronizedmap
结语
从HashMap可以扩展出很多面试题,例如并发问题,集合问题,队列问题等,这些将在后面的相关专题中一一介绍。
Java HashMap面试须知相关推荐
- 阿里 + 京东 Java 岗面试题概要(面试须知)
很多程序员都会担心 35 岁的职业危机,而数据也显示,40 岁以上的程序员几乎不存在,大都转了管理岗,余下的只能被迫离职或者转行.然而,太久待在舒适区,可能连小公司的面试,都很难通过了. 程序员是最需 ...
- 阿里 + 京东 Java 岗面试题概要(面试须知
Zookeeper 做服务的注册中心,如果服务规模大于 1000,会发生羊群效应网络风暴,怎么优化? 分布式事务两阶段提交如果第二阶段超时了怎么办? 从源码角度讲一讲 Eureka 的底层架构与设计原 ...
- 多线程面试题_100多线程和Java并发面试问答–最终清单(PDF下载)
多线程面试题 在这篇文章中,我们将提供有关多线程和Java并发面试问答的综合文章. 编者注:并发始终是开发人员的挑战,编写并发程序可能非常困难. 引入并发时,有很多事情可能会崩溃,并且系统的复杂性会大 ...
- 【Java核心面试宝典(1),程序员Javaweb源码
**这里就涉及到了一个链表中数据存储时,进行"树化"和"链化"的一个过程,**那么什么是"树化"和"链化"呢? 当我们在 ...
- 一次违反常规的Java大厂面试经历,内含福利
分享第一份Java基础-中级-高级面试集合 Java基础(对象+线程+字符+接口+变量+异常+方法) Java中级开发(底层+Spring相关+Redis+分布式+设计模式+MySQL+高并发+锁+线 ...
- Java HashMap工作原理深入探讨
大部分Java开发者都在使用Map,特别是HashMap.HashMap是一种简单但强大的方式去存储和获取数据.但有多少开发者知道 HashMap内部如何工作呢?几天前,我阅读了java.util.H ...
- java工程师面试如何自我介绍
首先进行自我介绍,如姓名.籍贯.学历,毕业院校: 接下来介绍工作情况,如:在哪工作过多久:工作内容是什么: 之后介绍下专业技能,挑选强项说明(切勿刚接触,之后乱说,否则面试者一问,马上穿帮). 备注: ...
- Java基础面试16问
来自三太子敖丙的Java基础面试16问 原文地址 ** 说说进程和线程的区别? ** 进程是程序的一次执行,是系统进行资源分配和调度的独立单位,他的作用是是程序能够并发执行提高资源利用率和吞吐率. 由 ...
- 美团科技 Java工程师_美团网java工程师面试都会问哪些问题?
美团网java工程师面试主要考察面试者的专业知识,涉及TCP/IP 线程.synHashMap底层.进程关系.servlet生命周期.pringMVC单例异常.Object方法.ConcurrentH ...
最新文章
- 四种类型的数据分析模式
- Flink 1.7.2 dataset transformation 示例
- ASP.NET MVC中如何在客户端进行必要的判断
- 从0到1搭建spark集群---企业集群搭建
- oracle中如何取消外键的,ORACLE中添加删除主键、外键
- 电脑遇到脱机状态怎么解除?
- java程序员推荐书籍
- json java 比较_Java中json工具对比分析
- 期刊论文发表有哪些方法
- 程序员面试智力题(六)
- h5/web遮罩弹窗
- HFDS 内部工作机制
- UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件
- 模板--二分图最大匹配
- python循环引用的解决办法
- Oxygen PDF Chemistry转换场景的处理器
- python模拟鼠标键盘点击,简单自动化动物餐厅
- nosql | 搭建mongodb副本集
- 12864c语言接温度计,单片机万年历+温度计+12864LCD液晶显示 仿真+程序
- linux svn e170001,jenkins - svn: E170001报错的原因以及解决方案
热门文章
- 进销存设计中的库存设计
- 3万行代码硬撸一个一键发布文章工具,简直不要太好用,从此写文章,发文章,太简单了好伐
- 若说耳机世界里有一股清流,那这款QCY耳机肯定是其中之一
- 【Java基础】Java综合练习
- 三款免费杀毒软件+clamAV
- 【U盘主控芯片的优缺点】
- QT 5.7 for iOS Xcode 8 Project ERROR: Xcode not set up properly. You may need to confirm the license
- C语言字符意思 char,c语言中char* 代表什么
- 《Effective Java》读书笔记五(枚举和注解)
- vue全家桶_vue脚手架