有关 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 的关系( ...
随机推荐
- 使用Docker发布blazor wasm
Blazor编译后的文件是静态文件,所以我们只需要一个支持静态页面的web server即可. 根据不同项目,会用不同的容器编排,本文已无网关的情况下为例,一步一步展示如何打包进docker 需求 H ...
- [Python基础]009.os模块(1)
os模块(1) 介绍 os 常量 文件目录操作 文件属性操作 遍历文件夹 介绍 os模块是系统服务应用程序接口,是Python最常用的模块之一. os模块包含了对文件和文件夹的操作,操作系统相关的操作 ...
- 经典卷积神经网络算法(3):VGG
.caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...
- Rocket - util - HeterogeneousBag
https://mp.weixin.qq.com/s/5hNM4yeQjaLvAJzgMG9PGQ 介绍HeterogeneousBag的实现. 1. 基本介绍 一个口袋(bag ...
- Java实现 LeetCode 791 自定义字符串排序(桶排序)
791. 自定义字符串排序 字符串S和 T 只包含小写字符.在S中,所有字符只会出现一次. S 已经根据某种规则进行了排序.我们要根据S中的字符顺序对T进行排序.更具体地说,如果S中x在y之前出现,那 ...
- Java实现蓝桥杯 算法提高 盾神与积木游戏
题目描述 最近的m天盾神都去幼儿园陪小朋友们玩去了~ 每个小朋友都拿到了一些积木,他们各自需要不同数量的积木来拼一些他们想要的东西.但是有的小朋友拿得多,有的小朋友拿得少,有些小朋友需要拿到其他 小朋 ...
- Java实现 蓝桥杯 算法训练 1的个数
试题 算法训练 1的个数 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 输入正整数n,判断从1到n之中,数字1一共要出现几次.例如1123这个数,则出现了两次1.例如15,那么从1 ...
- java实现第四届蓝桥杯连续奇数和
连续奇数和 题目描述 小明看到一本书上写着:任何数字的立方都可以表示为连续奇数的和. 比如: 2^3 = 8 = 3 + 5 3^3 = 27 = 7 + 9 + 11 4^3 = 64 = 1 + ...
- Linux脚本安装包
脚本安装包 并不是独立的软件安装包类型,常见安装的是源码包,是人为把安装过程写成了自动安装的脚本,只要执行脚本,定义简答的参数,就可以实现安装,非常类似于Windows下软件的安装方式. 安装过程(安 ...
- Navicat 连接远程服务器端MySQL
Navicat是一个很好的操作各种数据库的图形化工具,我用它在本地连接过MySQL.SQL Server.SQLite,用它操作数据库确实非常方便.快捷,再搭配SQL语句,是一个很好的选择了. 废话不 ...