HashMap 面试专题

news2024/9/23 3:26:14

1、HashMap 的底层结构

①JDK1.8 以前
JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的hashCode 函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
②JDK1.8
相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
image.png
拉链法
Java中HashMap 是利用“拉链法”处理 HashCode 的碰撞问题。在调用 HashMap 的 put 方法或 get 方法时,都会首先调用 hashcode 方法,去查找相关的 key,当有冲突时,再调用 equals 方法。hashMap 基于 hasing 原理,我们通过 put 和 get 方法存取对象。当我们将键值对传递给 put 方法时,他调用键对象的 hashCode() 方法来计算 hashCode,然后找到 bucket(哈希桶)位置来存储对象。当获取对象时,通过键对象的 equals() 方法找到正确的键值对,然后返回值对象。HashMap 使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中。hashMap 在每个链表节点存储键值对对象。

2、HashMap 的 put 和 get 方法

map.put(k,v) 实现原理:
①先将键值对 k,v 封装到 Node 对象中;
②底层会调用 k 的 hashCode() 方法得出 hash 值;
③通过哈希函数,将 hash 值转换为数组的下标;
④进行比较:下标位置如果没有任何元素,就把 Node 添加到这个位置上;如果下标位置上有链表,此时会拿着 k 和链表上的每一个节点的 k 用 equals() 方法进行比较(因为 Map 是不可重复的),如果没有重复的新节点就会加到链表末尾,否则则会覆盖有相同 k 值的节点。

map.get(k) 实现原理:
①调用 k 的 hashCode() 方法得出 hash 值;
②通过哈希函数,将 hash 值转换为数组的下标;
③进行比较:下标位置如果没有任何元素,返回 null,如果下标位置上有链表,此时会拿着 k 和链表上的每一个节点的 k 用 equals() 方法进行比较,如果结果都是 false,则返回 null;如果有一个节点用了equals 方法后结果为 true,则返回这个节点的 value 值。

3、为什么重写 equals() 就一定要重写 hashCode() 方法?

如果两个对象相同(即用 equals 比较返回 true),那么它们的 hashCode 值一定要相同!!!!;
如果两个对象不同(即用 equals 比较返回 false),那么它们的 hashCode 值可能相同也可能不同;
如果两个对象的 hashCode 相同(存在哈希冲突),那么它们可能相同也可能不同(即 equals 比较可能是false 也可能是 true);
如果两个对象的 hashCode 不同,那么他们肯定不同(即用 equals 比较返回 false)。

对于对象集合的判重,如果一个集合含有 10000 个对象实例,仅仅使用 equals() 方法的话,那么对于一个对象判重就需要比较 10000 次,随着集合规模的增大,时间开销是很大的。
但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的 hashCode 不相同,也不再需要调用 equals() 方法,从而大大减少了 equals()比较次数。
所以从程序实现原理上来讲的话,既需要 equals() 方法,也需要 hashCode() 方法。那么既然重写了equals(),那么也要重写 hashCode() 方法,以保证两者之间的配合关系。

我们在实际应用过程中,如果仅仅重写了 equals(),而没有重写 hashCode() 方法,会出现什么情况?
字段属性值完全相同的两个对象因为 hashCode 不同,所以在 hashMap 中的 table 数组的下标不同,从而这两个对象就会同时存在于集合中,所以重写 equals() 就一定要重写 hashCode() 方法。

4、HashMap 为什么线程不安全,想要线程安全怎么办?

HashMap 的线程不安全主要体现在下面两个方面:
①在 JDK1.7 中,当并发执行扩容操作时会造成环形链和数据丢失的情况。
②在 JDK1.8 中,在并发执行 put 操作时会发生数据覆盖的情况。

想要线程安全,可以使用 HashTable 或 ConcurrentHashMap。

5、HashMap 与 HashTable 的区别

①线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过 synchronized 修饰。(如果要保证线程安全的话就使用 ConcurrentHashMap 吧!);
② 效率:因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
③对 Null key 和 Null value 的支持: HashMap可以存储 null 的key和value,但null作为键只能有一个,null作为值可以有多个;HashTable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。
④初始容量大小和每次扩充容量大小的不同:1、创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16 。之后每次扩充,容量变为原来的 2 倍。2、 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小(HashMap 中的 tableSizeFor() 方法保证)。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
⑤底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8,将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。

6、HashMap 扩容

①首先判断 OldCap 有没有超过最大值;
②当 hashmap 中的元素个数超过 数组大小 * loadFactor 时,就会进行数组扩容,loadFactor 的默认值为 0.75,也就是说,默认情况下,数组大小为 16,那么当 hashmap 中元素个数超过 16 * 0.75=12 的时候,就把数组的大小扩展为 2 * 16=32,即扩大一倍;
③然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知 hashmap 中元素的个数,那么预设元素的个数能够有效的提高 hashmap 的性能。比如说,我们有 1000 个元素 new HashMap(1000), 但是理论上来讲 new HashMap(1024) 更合适,不过上面已经说过,即使是 1000,hashmap 也自动会将其设置为 1024;
但是 new HashMap(1024) 还不是更合适的,因为 0.75*1000 < 1000, 也就是说为了让 0.75 * size > 1000, 我们必须这样 new HashMap(2048)才最合适,既考虑了 & 的问题,也避免了 resize 的问题。

7、为什么是 2 的幂次方

我们首先可能会想到采用 % 取余的操作来实现。但是,重点来了 :“取余 (%) 操作中如果除数是 2 的幂次则等价于与其除数减一的与 (&) 操作(也就是说 hash % length == hash & (length - 1)的前提是 length 是 2 的 n 次方)。” 并且采用二进制位操作 &,相对于 % 能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。

8、为什么会产生 Hash 碰撞

key 值无界(任意的),计算出对应的 hash 值有界。

9、红黑树

①为什么使用红黑树,不使用其他的树?
因为红黑树不追求完美的平衡,只要求达到部分平衡,可以减少增删结点时的左旋转和右旋转的次数。

②为什么 JDK8 中 HashMap 底层使用红黑树?
因为链表查询的时间复杂度是 O(n),红黑树的时间复杂度为 O(logn)。可以提升效率。

③为什么链表长度大于阈值 8 时才将链表转为红黑树?
因为树结点占用的存储空间是普通结点的两倍。因此红黑树比链表更加耗费空间。结点较少的时候,时间复杂度上链表不会比红黑树高太多,但是能大大减少空间。
当链表元素个数大于等于 8 时,链表换成树结构;若桶中链表元素个数小于等于 6 时,树结构还原成链表。因为红黑树的平均查找长度是 log(n),长度为 8 的时候,平均查找长度为 3,如果继续使用链表,平均查找长度为 8/2=4,这才有转换为树的必要。

④当长度减小的时候红黑树还会转换为链表吗
如果 loHead 不为空,且链表长度小于等于 6 ,则将红黑树转成链表。

10、HashMap 的长度为什么是 2 的幂次方

“取余 (%) 操作中如果除数是 2 的幂次则等价于与其除数减一的与 (&) 操作(也就是说 hash % length == hash & (length - 1)的前提是 length 是 2 的 n 次方)。” 并且采用二进制位操作 &,相对于 % 能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。

11、ConcurrentHashMap 和 HashTable 的区别

底层数据结构: JDK1.7 的 ConcurrentHashMap 底层采用分段的数组+链表实现,JDK1.8 采用的数据结构跟 HashMap1.8 的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用数组+链表的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
实现线程安全的方式(重要)
① 在 JDK1.7 的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段 (Segment) ,每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率,在读的过程中,是不会进行加锁的。 JDK1.8 的时候已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6 以后对 synchronized 锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;
② Hashtable(同一把锁):使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈,效率越低。
image.png
JDK1.8 的 ConcurrentHashMap(TreeBin:红黑树结点,Node:链表节点)。ConcurrentHashMap 取消了 Segment 分段锁,采用 CAS 和 synchronized 来保证并发安全。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N)))
synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。

加油

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/373036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot知识快速复习

Spring知识快速复习启动器自动装配ConfigurationImport导入组件Conditional条件装配ImportResource导入Spring配置文件ConfigurationProperties配置绑定Lombok简化开发dev-toolsyaml请求和响应处理静态资源规则与定制化请求处理-Rest映射请求处理-常用参数注解使用请求处理-Ser…

程序员在小公司(没有大牛,人少)怎么成长?

大多数小公司都是创业公司&#xff0c;所以它们有着非常独特的“创业心态”。所谓创业心态通常表现为关注快速增长&#xff0c;竭尽所能让公司盈利&#xff0c;或者达成其他一些迫切目标。 在这样一家公司工作的软件开发人员&#xff0c;你极有可能要身兼多职&#xff0c;不能…

拼数(一般贪心)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题号&#xff1a;NC16783 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 262144K&#xff0c;其他语言524288K 64bit IO Format: %lld 题目描述 设有n个正整…

架构初探-学习笔记

1 什么是架构 有关软件整体结构与组件的抽象描述&#xff0c;用于指导软件系统各个方面的设计。 1.1 单机架构 所有功能都实现在一个进程里&#xff0c;并部署在一台机器上。 1.2 单体架构 分布式部署单机架构 1.3 垂直应用架构 按应用垂直切分的单体架构 1.4 SOA架构 将…

华为OD机试题,用 Java 解【停车场车辆统计】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

基于node.js+vue开发的学生考勤管理系统84y43

目录 1 绪论 6 1.1 课题背景 6 1.2 课题研究现状 6 1.3 初步设计方法与实施方案 7 1.4 本文研究内容 7 2 系统开发环境 9 2.1 vue简介 9 2.3 B/S结构简介 9 2.4MySQL数据库 10 3 系统分析 11 3.1 系统可行性分析 11 3.1.1 经济可行性 11…

2023淘宝天猫38节红包满减优惠活动时间是从几月几号什么时候开始?

2023年淘宝天猫38节活动将于2023年3月2日中午12点正式开始&#xff0c;活动将持续至2023年3月8日晚上23点59分。届时&#xff0c;淘宝天猫将推出一系列的优惠活动和红包福利&#xff0c;为广大女性用户送上节日的祝福和福利。在这个特别的节日里&#xff0c;淘宝天猫为女性用户…

数据结构---单链表

专栏&#xff1a;数据结构 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;从零开始&#xff0c;数据结构&#xff01;&#xff01; 单链表前言顺序表的缺陷链表的概念以及结构链表接口实现打印链表中的元素SLTPrintphead->next!NULL和phead!NULL的区别开辟空间SLTNewNod…

多分类、正则化问题

多分类问题 利用逻辑回归解决多分类问题&#xff0c;假如有一个训练集&#xff0c;有 3 个类别&#xff0c;分别为三角形 &#x1d466; 1&#xff0c;方框&#x1d466; 2&#xff0c;圆圈 &#x1d466; 3。我们下面要做的就是使用一个训练集&#xff0c;将其分成 3 个二…

计算机图形学:liang算法和Cyrus-Beck算法

其中Cyrus-Beck算法呢&#xff0c;是计算一根直线一个多边形的交线段&#xff1b;liang算法是Cyrus的一个特例&#xff0c;即多边形刚好是矩形&#xff1b;先看看Cyrus算法的思路【从别的博客找的图片】&#xff1a;这很容易理解&#xff0c;点积>0时就可能中内部嘛&#xf…

使用pynimate制作动态排序图

大家好&#xff0c;数据可视化动画使用Python包就可以完成&#xff0c;效果如下&#xff1a;想要使用Pynimate&#xff0c;直接import一下就行&#xff1a;import pynimate as nim输入数据后&#xff0c;Pynimate将使用函数Barplot&#xff08;&#xff09;来创建条形数据动画。…

Apollo(阿波罗)分布式配置安装详解

Apollo&#xff08;阿波罗&#xff09; Apollo&#xff08;阿波罗&#xff09;是携程框架部门研发的分布式配置中心&#xff0c;能够集中化管理应用不同环境、不同集群的配置&#xff0c;配置修改后能够实时推送到应用端&#xff0c;并且具备规范的权限、流程治理等特性&#…

【网络】套接字 -- UDP

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【网络】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文章…

远程使用服务器上的Jupyter notebook

记录下如何远程使用服务器上的jupyter notebook。 主要是在服务器端执行以下操作&#xff1a; 激活需要使用的环境使用pip list 或conda list检查是否已经安装notebook。如果没有安装&#xff0c;则使用pip install jupyter notebook进行安装&#xff1b;反之忽略这一步&…

HDMI协议介绍(四)--Video

目录 视频格式 RGB444 YUV444 YUV422 YUV420 Color Depth Video控制信号 Pixel Repetition HDMI支持多种视频格式和分辨率。以hdmi1.4和2.0协议来说&#xff0c;视频格式支持RGB444、YUV444、YUV422和YUV420&#xff0c;其中RGB444和YUV444一般都是要求支持的。 视频格式…

【计算机网络】网络层IP协议

文章目录一、认识IP协议二、IP协议头部格式三、IP地址划分1. IP地址分类2. 子网划分四、IP地址数量危机1. IP地址的数量限制2. NAT技术五、私网IP和公网IP六、路由1. 认识路由2. 路由表生成算法一、认识IP协议 IP协议是Internet Protocol&#xff08;互联网协议&#xff09;的…

Centos7 安装jenkins java1.8版本

1. 首先安装好jdk1.8 2. 安装jenkins 命令&#xff1a;(可以在根目录&#xff0c;创建文件夹 mkdir home 然后在此文件夹下操作 cd /home) a 清华源&#xff0c;获取jenkins安装包 wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat/jenkins-2.346-1.1.noarch.rp…

2023软件测试金三银四常见的软件测试面试题-【测试理论篇】

三、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段&#xff1a;需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的SE会把需求文档给我们自己先去了解一到两天这样&#xff0c;之后我们会有一个需求澄清会议&#xff0c; 我…

GO进阶(4) 深入Go的内存管理

Go语言成为高生产力语言的原因之一自己管理内存&#xff1a;Go抛弃了C/C中的开发者管理内存的方式&#xff0c;实现了主动申请与主动释放管理&#xff0c;增加了逃逸分析和GC&#xff0c;将开发者从内存管理中释放出来&#xff0c;让开发者有更多的精力去关注软件设计&#xff…

1633_xv6 book PC硬件与BootLoader

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 按照课程的建议&#xff0c;先去读了一下xv6 book的附录&#xff0c;感觉还是有一些收获的。这中间去扫盲增补各种概念的过程就已经收获不少。 1. 这里介绍了一下计…