有关 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 的关系( ...
随机推荐
- 2020由浅入深最强Java面试题
1.String,StringBuffer和StringBuilder有什么区别? String是字符串常量,不可变对象,每次对String修改都等同于生成了一个新的String象,然后将指针 ...
- h5中嵌入视频自动播放的问题
在H5页面中嵌入视频的情况是比较多件的,有时候会碰到需要自动播放的情况,之前根本觉得这不是问题,但是自己的项目中需要视频的时候就有点sb了,达不到老板的要求,那个急呀~~~ 各种查资料,找到一个方法, ...
- protocbuf的简单理解
之前通信协议替换为protocbuf!新老交替,很多不同看法,也提出来一些负面因数: 1.老的内部通信协议体已经有一段时间了,稳定熟悉! 2.通过通信结构体进行交互,实际上并没有序列化和反序列化的过程 ...
- Misdirection: 1靶机writeup
看下端口 nmap -A 172.16.61.131 一些坑3306无法访问,80,web2py漏洞无法利用 利用dirb遍历网站路径 得到下面命令执行漏洞 http://172.16.61.131: ...
- 公司如何通过电脑监控来规范员工行为-iis7服务器管理功能教程
现在员工办公基本都用电脑,而电脑不仅仅可以用来处理工作,还可以上网.娱乐等.往往很多员工的自制能力比较差,在工作时间和人聊天,浏览购物网站,看新闻,炒股等休闲娱乐上花费大量的时间,导致工作效率降低,影 ...
- ThreadLocal Thread ThreadLocalMap 之间的关系
ThreadLocal :每个线程通过此对象都会返回各自的值,互不干扰,这是因为每个线程都存着自己的一份副本.需要注意的是线程结束后,它所保存的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用) ...
- 使用RTL进行硬件模型编程的局限性
https://mp.weixin.qq.com/s/Nj_d3hwgNX4kWVtKsqMSWg 硬件模型编程,即Hardware Model Programming.在RTL抽象级别 ...
- Chisel3 - 基本数据类型
https://mp.weixin.qq.com/s/bSrM-wLRn7O_75xYKeoaEQ Chisel中的基本数据类型,不是Verilog中的Wire和Reg.Wire和Register ...
- 点击label时text输入框被选中
当点击label标签时,能够选中与其对应的输入框,有2种方式,采用第一种时会产生太多的id,推荐第二种. <form action="03.php"> <labe ...
- Spring 内部方法调用失效问题(AOP)
AOP使用的是动态代理的机制,它会给类生成一个代理类,事务的相关操作都在代理类上完成.内部方式使用this调用方式时,使用的是实例调用,并没有通过代理类调用方法,所以会导致事务失效. 解决办法 方式一 ...