有关 HashMap 面试会问的一切
前言
HashMap 是无论在工作还是面试中都非常常见常考的数据结构。
比如 Leetcode 第一题 Two Sum 的某种变种的最优解就是需要用到 HashMap 的,高频考题 LRU Cache 是需要用到 LinkedHashMap 的。
HashMap 用起来很简单,底层实现也不复杂,先来看几道常见的面试题吧。相信大家多多少少都能回答上来一点,不清楚的地方就仔细阅读本文啦~这篇文章带你深挖到 HashMap 的老祖宗,保证吊打面试官
- == 和 equals() 的区别?
- 为什么重写 equals() 就必须要重写 hashCode()?
- Hashtable, HashSet 和 HashMap 的区别和联系
- 处理 hash 冲突有哪些方法?Java 中用的哪一种?为什么?另一种方法你在工作中用过吗?在什么情 况下用得多?
- 徒手实现一个 HashMap 吧
本文分以下章节:
- Set 和 Map 家族简介
- HashMap 实现原理
- 关于 hashCode() 和 equals()
- 哈希冲突详解
- HashMap 基本操作
- 高频面试考题分析
Set 家族
在讲 Map 之前,我们先来看看 Set。
集合的概念我们初中数学就学过了,就是里面不能有重复元素,这里也是一样。
Set 在 Java 中是一个接口,可以看到它是 java.util 包中的一个集合框架类,具体的实现类有很多:

其中比较常用的有三种:
HashSet: 采用 Hashmap 的 key 来储存元素,主要特点是无序的,基本操作都是 O(1) 的时间复杂度,很快。
LinkedHashSet: 这个是一个 HashSet + LinkedList 的结构,特点就是既拥有了 O(1) 的时间复杂度,又能够保留插入的顺序。
TreeSet: 采用红黑树结构,特点是可以有序,可以用自然排序或者自定义比较器来排序;缺点就是查询速度没有 HashSet 快。
Map 家族
Map 是一个键值对 (Key - Value pairs),其中 key 是不可以重复的,毕竟 set 中的 key 要存在这里面。
那么与 Set 相对应的,Map 也有这三个实现类:
HashMap: 与 HashSet 对应,也是无序的,O(1)。
LinkedHashMap: 这是一个「HashMap + 双向链表」的结构,落脚点是 HashMap,所以既拥有 HashMap 的所有特性还能有顺序。
TreeMap: 是有序的,本质是用二叉搜索树来实现的。
HashMap 实现原理
对于 HashMap 中的每个 key,首先通过 hash function 计算出一个 hash 值,这个hash值就代表了在 buckets 里的编号,而 buckets 实际上是用数组来实现的,所以把这个数值模上数组的长度得到它在数组的 index,就这样把它放在了数组里。

那么这里有几个问题:
如果不同的元素算出了相同的哈希值,那么该怎么存放呢?
答:这就是哈希碰撞,即多个 key 对应了同一个桶。
HashMap 中是如何保证元素的唯一性的呢?即相同的元素会不会算出不同的哈希值呢?
答:通过 hashCode() 和 equals() 方法来保证元素的唯一性。
如果 pairs 太多,buckets 太少怎么破?
答:Rehasing. 也就是碰撞太多的时候,会把数组扩容至两倍(默认)。所以这样虽然 hash 值没有变,但是因为数组的长度变了,所以算出来的 index 就变了,就会被分配到不同的位置上了,就不用挤在一起了,小伙伴们我们江湖再见~
那什么时候会 rehashing 呢?也就是怎么衡量桶里是不是足够拥挤要扩容了呢?
答:load factor. 即用 pair 的数量除以 buckets 的数量,也就是平均每个桶里装几对。Java 中默认值是 0.75f,如果超过了这个值就会 rehashing.
关于 hashCode() 和 equals()
如果 key 的 hashCode() 值相同,那么有可能是要发生 hash collision 了,也有可能是真的遇到了另一个自己。那么如何判断呢?继续用 equals() 来比较。
也就是说,
hashCode() 决定了 key 放在这个桶里的编号,也就是在数组里的 index;
equals() 是用来比较两个 object 是否相同的。
那么该如何回答这道经典面试题:
为什么重写 equals() 方法,一定要重写 hashCode() 呢?
答:首先我们有一个假设:任何两个 object 的 hashCode 都是不同的。
那么在这个条件下,有两个 object 是相等的,那如果不重写 hashCode(),算出来的哈希值都不一样,就会去到不同的 buckets 了,就迷失在茫茫人海中了,再也无法相认,就和 equals() 条件矛盾了,证毕。
撒花~~
接下来我们再对这两个方法一探究竟:
其实 hashCode() 和 equals() 方法都是在 Object class 这个老祖宗里定义的,Object 是所有 Java 中的 class 的鼻祖,默认都是有的,甩不掉的。
那既然是白给的,我们先来看看大礼包里有什么,谷歌 Object 的 Oracle 文档:

所以这些方法都是可以直接拿来用的呢~
回到 hashCode() 和 equals(),那么如果这个新的 class 里没有重写 (override) 这两个方法,就是默认继承 Object class 里的定义了。
那我们点进去来看看 equals() 是怎么定义的:

记笔记:
equals() 方法就是比较这两个 references 是否指向了同一个 object.
嗯???你在逗我吗??那岂不是和 == 一样了??
补充:
我们常用的比较大小的符号之 ==
如果是 primitive type,那么 == 就是比较数值的大小;
如果是 referencetype,那么就比较的是这两个 reference 是否指向了同一个 object。
再补充:
Java 的数据类型可以分为两种:
Primitive type 有且仅有8种:
byte, short, int, long,float, double, char, boolean. 其他都是 Reference type.
所以虽然 Java 声称 “Everything is object”,但是还是有非 object 数据类型的存在的。
我不信,我要去源码里看看它是怎么实现的。

哈,还真是的,绕了这么半天,equals() 就是用 == 来实现的!
那为什么还弄出来这么个方法呢?
答:为了让你 override~
比如一般来说我们比较字符串就是想比较这两个字符串的内容的,那么:
str1 = “tianxiaoqi”;
str2 = new String(“tianxiaoqi”);
str1 == str2; // return false
str1.equals(str2); // return true
因为 String 里是重写了 equals() 方法的:

老祖宗留给你就是让你自己用的,如果你不用,那人家也提供了默认的方法,也是够意思了。
好了,我们再去看 hashCode() 的介绍:

结论就是:
返回的并不一定是对象的(虚拟)内存地址,具体取决于运行时库和JVM的具体实现。 但无论是怎么实现的,都需要遵循文档上的约定,也就是对不同的
object 会返回唯一的哈希值。
哈希冲突详解
一般来说哈希冲突有两大类解决方式[2]
- Separate chaining
- Open addressing
Java 中采用的是第一种 Separate chaining,即在发生碰撞的那个桶后面再加一条“链”来存储,那么这个“链”使用的具体是什么数据结构,不同的版本稍有不同:
在 JDK1.6 和 1.7 中,是用链表存储的,这样如果碰撞很多的话,就变成了在链表上的查找,worst case 就是 O(n);
在 JDK 1.8 进行了优化,当链表长度较大时(超过 8),会采用红黑树来存储,这样大大提高了查找效率。
(话说,这个还真的喜欢考,已经在多次面试中被问过了,还有面试官问为什么是超过“8”才用红黑树
有关 HashMap 面试会问的一切的更多相关文章
- HashMap面试必问的6个点,你知道几个?
一.HashMap的实现原理? 此题可以组成如下连环炮来问 你看过HashMap源码嘛,知道原理嘛? 为什么用数组+链表? hash冲突你还知道哪些解决办法? 我用LinkedList代替数组结构可以 ...
- 一万三千字的HashMap面试必问知识点详解
目录 概论 Hasmap 的继承关系 hashmap 的原理 解决Hash冲突的方法 开放定址法 再哈希法 链地址法 建立公共溢出区 hashmap 最终的形态 Hashmap 的返回值 HashMa ...
- HashMap面试必问的数据结构相关知识
如果在看这篇文章时,对HashMap的结构还不是很了解,建议你参考前段时间写的<刨死你系列——HashMap剖析(基于jdk1.8)>,可能会对下面的提及到知识点有些帮助. 1:HashM ...
- Java面试必问之Hashmap底层实现原理(JDK1.7)
1. 前言 Hashmap可以说是Java面试必问的,一般的面试题会问: Hashmap有哪些特性? Hashmap底层实现原理(get\put\resize) Hashmap怎么解决hash冲突? ...
- 面试官问:HashMap在并发情况下为什么造成死循环?一脸懵
这个问题是在面试时常问的几个问题,一般在问这个问题之前会问Hashmap和HashTable的区别?面试者一般会回答:hashtable是线程安全的,hashmap是线程不安全的. 那么面试官就会紧接 ...
- 最近找java实习面试被问到的东西总结(Java方向)
时间,就是这么很悄悄的溜走了将近两个年华,不知不觉的,研二了,作为一个一般学校的研究生,不知道该说自己是不学无术,还是说有过努力,反正,这两年里,有过坚持,有过堕落,这不,突然间,有种开窍的急迫感,寻 ...
- 面试被问烂的 Spring IOC(求求你别再问了)
广义的 IOC IoC(Inversion of Control) 控制反转,即"不用打电话过来,我们会打给你". 两种实现: 依赖查找(DL)和依赖注入(DI). IOC 和 D ...
- JAVA程序员面试30问(附带答案)
第一,谈谈final, finally, finalize的区别. 最常被问到.final修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承.因此一个类不能 ...
- 面试常问Spring IOC,不得不会。
广义的 IOC IoC(Inversion of Control) 控制反转,即“不用打电话过来,我们会打给你”. 两种实现: 依赖查找(DL)和依赖注入(DI). IOC 和 DI .DL 的关系( ...
随机推荐
- 洛谷 P1352 没有上司的舞会 树形DP板子
luogu传送门 题目描述: 某大学有n个职员,编号为1~n. 他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司. 现在有个周年庆宴会,宴会每邀请来一个职员都会 ...
- eatwhatApp开发实战(九)
之前我们为app在item项上添加了点击出现修改对话框,对店名进行修改的功能,其中我们会发现我们点击item和点击item上的按钮会有点击冲突.这次我们来修正下这个问题,同时介绍item项的长按点击O ...
- [PHP学习教程 - 系统]001.引用文件(require & include)
引用文件的方法有两种:require 及 include.两种方式提供不同的使用弹性. 1.require 的使用方法如 require("MyRequireFile.php"); ...
- 开心一下-实现基于Java一个中文编程语言
https://mp.weixin.qq.com/s/TsTiLVF5D07-wbDMk9bsyQ 这不是认真的,不是真的要去实现一个中文编程语言. 多年以前,有位同学把Java代码发给我说帮 ...
- Rocket - debug - TLDebugModuleInner - COMMAND
https://mp.weixin.qq.com/s/Lz_D43YdhbRhiGiyoCBxDg 简单介绍TLDebugModuleInner中COMMAND寄存器的实现. 1. COMMANDRe ...
- jchdl - GSL实例 - Add
https://mp.weixin.qq.com/s/6xcYYdYZTBPTf25xFluzBQ 使用FullAdder级联实现加法器 参考链接: https://github.com/wj ...
- jchdl - GSL实例 - Register
https://mp.weixin.qq.com/s/uD5JVlAjTHQus2pnzPrdLg 多个D触发器可以组成一组寄存器. 摘自康华光<电子技术基础 · 数字部分>(第 ...
- 关于MySQL无法启动的一点问题
今天早上做作业启动MySQL遇到服务无法启动的问题,在网上找方法解决找了一个钟. 修改host文件,重装mysql服务,就差卸载重装了.到后面终于找到问题,原来是我之前改了root密码,所以每次启动m ...
- Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
730. 统计不同回文子字符串 给定一个字符串 S,找出 S 中不同的非空回文子序列个数,并返回该数字与 10^9 + 7 的模. 通过从 S 中删除 0 个或多个字符来获得子字符序列. 如果一个字符 ...
- Java实现 蓝桥杯 算法训练 最小乘积
算法训练 最小乘积(基本型) 时间限制:1.0s 内存限制:512.0MB 问题描述 给两组数,各n个. 请调整每组数的排列顺序,使得两组数据相同下标元素对应相乘,然后相加的和最小.要求程序输出这个最 ...