面试题分享之Java集合篇(三)
系列文章目录
- 面试题分享之Java基础篇(二)
- 面试题分享之Java基础篇(三)
面试题分享之Java集合篇(一)、
面试题分享之Java集合篇(二)
前言
哈喽,小伙伴们,昨天我们见识了HaspMap常见的面试题,如:HaspMap的get、put、resize方法的原理等等,废话不多说,今天继续来给大家分享几道Java集合的高频面试题。🌈
一、HashMap怎么计算 key 的 hash 值的?
先贴上源码(jdk1.8为例):
/**这是官方注释计算 key.hashCode() 并将 (XOR) 更高的哈希位传播到更低的哈希位。由于该表使用二次幂掩码,因此仅比当前掩码高位变化的哈希集将始终发生冲突。(在已知的例子中,有一组浮点键在小表中保存连续的整数。因此,我们应用了一个变换,将更高位的影响向下传播。在速度、实用性和比特扩展质量之间需要权衡。由于许多常见的哈希集已经合理分布(因此不会从扩展中受益),并且由于我们使用树来处理 bin 中的大量冲突,因此我们只是以最便宜的方式对一些移位进行异或运算,以减少系统损失,并合并最高位的影响,否则由于表边界,这些比特永远不会用于索引计算。*/static final int hash(Object key) {int h;/*如果key是null,则直接返回0作为哈希值如果不为null,返回原hashCode值和原hashCode值无符号右移16位的值按位异或的结果按位异或:将两个十进制数先转化为二进制数,相同的就取0,不同的就取1例如:15 跟 16 按位异或16 1 0 0 0 0 842115 ^ 0 1 1 1 1------------1 1 1 1 1 ---> 31*/return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
- 右移16位,正好是32位的一半,高半区和低半区做异或操作,就是为了混合原始哈希码的高位与地位,以此来加大低位的随机性。
- 设计者考虑到现在hashCode分布的已经不错了,而且当发生较大碰撞时也用树形存储降低了冲突,仅仅异或一下,少了系统的开销,也不会造成因为高位没有参与下标的计算,从而引起碰撞。
二、HashMap如何解决hash冲突
不清楚什么是hash冲突小伙伴可以参考:
面试题分享之Java基础篇(二)-CSDN博客
1、链地址法(也称为拉链法):
- 当两个或多个键的哈希值相同时,它们不会被存储在同一个桶(bucket)中,而是会被链接到同一个桶内的链表中。
HashMap
在内部使用Node<K,V>[]
数组来存储桶,每个桶可以是一个链表或红黑树(在Java 8及更高版本中,当链表长度超过某个阈值时,会转换为红黑树以提高性能)。- 当发生哈希冲突时,新的键值对将被添加到相应桶的链表或红黑树的末尾。
2、开放地址法:
- 这种方法并不是
HashMap
直接使用的,但在其他哈希表实现中可能会看到。 - 当发生哈希冲突时,会按照一定的规则(如线性探测、平方探测等)在哈希表中寻找下一个空闲位置来存储键值对。
3、再哈希法:
- 这不是
HashMap
的标准做法,但在某些哈希表实现中可能会使用。 - 当通过第一个哈希函数计算得到的哈希值发生冲突时,使用第二个哈希函数再次计算哈希值,并尝试将键值对存储在新的位置。如果仍然冲突,可以继续使用更多的哈希函数。
4、建立公共溢出区:
- 这种方法也不是
HashMap
的标准做法。 - 当哈希表的主区域无法容纳更多的键值对时,可以将它们存储在一个单独的溢出区域中。然而,在
HashMap
中,当容量不足时,它会通过重新分配更大的数组并进行重新哈希来扩展其容量。
对于
HashMap
来说,它主要依赖链地址法(拉链法)来解决哈希冲突。当向HashMap
中插入新的键值对时,它会首先计算键的哈希值,并使用该哈希值来确定应该将其存储在哪个桶中。如果该桶已经存在其他键值对(即发生了哈希冲突),则将新的键值对添加到该桶的链表或红黑树的末尾。
此外,为了优化性能并减少哈希冲突的可能性,HashMap
还使用了以下技术:
- 哈希函数:
HashMap
使用了一个精心设计的哈希函数来计算键的哈希值。这个函数试图将键均匀地分布到不同的桶中,以减少哈希冲突的可能性。 - 动态扩容:当
HashMap
中的键值对数量超过其容量的一定比例(称为加载因子,默认为0.75)时,它会自动扩容其内部数组的大小,并重新哈希所有键值对以确保它们仍然被正确地分配到新的桶中。这有助于减少哈希冲突并提高性能。
三、HashMap多线程操作导致死循环问题知道吗?
这个问题主要源于
HashMap
的扩容机制和链表或红黑树的节点转移过程。在扩容时,HashMap
会创建一个新的数组,并重新计算每个键的哈希值以确定它们在新数组中的位置。这个过程需要遍历原数组中的所有桶(bucket),并将桶中的链表或红黑树节点转移到新数组对应的桶中。如果在这个过程中发生并发修改(例如,另一个线程插入或删除了键值对),就可能导致节点在新旧数组之间形成循环引用,进而引发死循环。具体来说,如果两个线程同时对一个桶进行扩容操作,并且其中一个线程在遍历链表或红黑树的过程中修改了链表或红黑树的结构(例如,删除了某个节点),那么另一个线程在继续遍历时就可能会遇到已经遍历过的节点,从而形成循环引用。
四、说说HashMap 和 HashSet 区别?
HashSet
底层就是基于 HashMap
实现的。两者主要区别:
HashSet | HshMap | |
数据结构和存储方式 | 实现Set接口,HashSet内部使用哈希表来存储元素,并通过元素的哈希码来判断是否重复 | 实现Map接口,HashMap存储的是键值对,键(key)是唯一的,值(value)可以重复 |
元素类型和唯一性 | 存储的是单一的元素类型(如整数、字符串等),并且元素必须是唯一的,不会存在重复的元素 | 存储的是键值对,键和值可以是不同类型的对象。键必须是唯一的,而值可以重复 |
查找速度 | 速度相对较慢 | 速度相对较快 |
五、ConcurrentHashMap在Jdk1.7和Jdk1.8的实现原理
1、ConcurrentHashMap跟HashMap的区别
- HashMap的底层数据结构主要是数组+链表(确切的说是由链表为元素的数组),ConcurrentHashMap的底层数据结构在JDK 1.7中是基于Segment数组+HashEntry数组+链表,而在JDK 1.8中则改为了Node+红黑树。
- HashMap是非线程安全的,它不能在多线程环境下正确工作。当多个线程同时对HashMap进行修改时,可能会导致数据不一致或者抛出异常。而ConcurrentHashMap是线程安全的,它使用了锁分段技术(lock striping)来实现并发安全性,允许多个线程在不同的段上并发地进行修改操作。
2、在JDK1.7实现原理
在JDK1.7中ConcurrentHashMap采用数组+Segment+分段锁的方式实现。
什么是Segment呢?
ConcurrentHashMap中的分段锁称为Segment.它类似HashMap的结构,内部拥有一个Entry数组,数组中的每个元素又是一个链表,同时又是一个ReentrantLock(Segment继承了ReentrantLock)
Concurrent使用分段锁机制,将数据一段一段的存储,然后给每一段数据加锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据可以被其他线程访问,能够实现真正的并发访问。
Concurrent的扩容机制:当某个 Segment
中的元素数量超过其容量阈值时,会触发扩容操作。扩容操作会创建一个新的 Segment
数组,并将原有的 Segment
中的元素重新分配到新的 Segment
中。这个过程中会涉及到大量的数据迁移和重哈希操作,因此是一个耗时的过程。但由于采用了分段锁机制,扩容操作可以并行进行,从而提高了性能。
ConcurrentHashMap定位一个元素需要经过两次hash过程:第一次定位到Segmnent,第二次定位到元素所在的链表的头部。
该结构的优缺点:
缺点:Hash的过程要比普通的HashMap要长
优点:写操作时只对元素所在的Segment进行加锁即可,不会影响到其他Segment,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(写操作非常平均的分布在所有Segment上)。所以,通过这种结构,ConcurrentHashMap并发能力大大提高。
3、在JDK1.8实现原理
在 JDK 1.8 中,ConcurrentHashMap
的实现发生了较大的变化,主要采用了CAS(Compare-And-Swap)操作、Synchronized关键字以及Node+红黑树的存储结构。
- CAS 操作:CAS 是一种无锁算法,它通过比较并交换操作来实现对共享变量的原子操作。在
ConcurrentHashMap
中,CAS 操作被用于实现节点的插入、更新和删除等操作。由于 CAS 操作具有原子性,因此可以保证多线程环境下的数据一致性。- Synchronized:虽然 JDK 1.8 中的
ConcurrentHashMap
摒弃了分段锁机制,但它仍然使用了Synchronized
关键字来保证对共享变量的同步访问。具体来说,ConcurrentHashMap
的每个节点(Node)在更新数据时都会使用Synchronized
来保证操作的原子性。- Node+红黑树:在 JDK 1.8 中,
ConcurrentHashMap
的内部存储结构由Segment+HashEntry
改为了Node+红黑树
。具体来说,当某个桶(bucket)中的链表长度超过一定的阈值(默认为 8)时,会将该链表转换为红黑树,以提高查询效率。红黑树是一种自平衡的二叉搜索树,它的查询、插入和删除操作的时间复杂度都是 O(logN)。
总结
这两期的面试题都偏理论性的,要求小伙伴有很好的数据结构的基础,深入源码分析,多理解不要死记硬背。好了,今天的分享就到这里,拜拜。
相关文章:
面试题分享之Java集合篇(三)
注意:文章若有错误的地方,欢迎评论区里面指正 🍭 系列文章目录 面试题分享之Java基础篇(二)面试题分享之Java基础篇(三) 面试题分享之Java集合篇(一)、 面试题分享之Ja…...

【python】模拟巴特沃斯滤波器
巴特沃斯滤波器(Butterworth Filter),以其设计者斯蒂芬巴特沃斯(Stephen Butterworth)的名字命名,是一种具有平滑频率响应的滤波器。这种滤波器在频域中具有非常平坦的无波纹响应,直到它达到截止…...
面试题:简述Go的垃圾回收机制
Go的GC(Garbage Collection, 垃圾回收)机制主要是用来自动释放不再被程序使用的内存,以防止内存泄漏。Go的垃圾回收是并发的,也就是说,它在主程序运行的同时进行垃圾回收。 1. 标记清除(Mark and Sweep) Go的垃圾回收器主要使用的是标记清除…...

Vue、React实现excel导出功能(三种实现方式保姆级讲解)
第一种:后端返回文件流,前端转换并导出(常用,通常公司都是用这种方式) 第二种:纯后端导出(需要了解) 第三种:纯前端导出(不建议使用,数据处理放…...

初识C语言——第十六天
C语言中的语句结构类型:顺序/选择/循环 分支语句 if else switch 循环语句 while for do whlie goto语句 代码练习:找两个整数的最大公约数和最小公倍数 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h>//int main() //{ // int age 60; // if (ag…...

Vue的省份联动
Vue的省份联动 一、安装依赖库 npm install element-china-area-data -Snpm install element-ui --save全局使用elemntui组件库 import ElementUI from element-ui; import element-ui/lib/theme-chalk/index.css;Vue.use(ElementUI);二 、代码如下 <template><div…...

element-ui skeleton 组件源码分享
今日简单分享 skeleton 骨架屏组件源码,主要从以下四个方面来讲解: 1、skeleton 组件的页面结构 2、skeleton 组件的属性 3、skeleton item 组件的属性 4、skeleton 组件的 slot 一、skeleton 组件的页面结构 二、skeleton 组件的属性 2.1 animate…...

深度学习:基于TensorFlow、Keras,使用长短期记忆神经网络模型(LSTM)对Microsoft股票进行预测分析
前言 系列专栏:机器学习:高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目,每个项目都处理一组不同的问题,包括监督和无监督学习、分类、回归和聚类,而且涉及创建深度学…...

【websocket-客户端可视化工具】
postman 新版postman (版本v11以上) ,除了http协议,还支持了Websocket,MQTT,gRPC等多种连接协议,可以作为多种协议的客户端,使用起来非常方便。 使用 服务端代码 这里以websocket协议举例,代…...

STC8增强型单片机开发——C51版本Keil环境搭建
一、目标 了解C51版本Keil开发环境的概念和用途掌握C51版本Keil环境的安装和配置方法熟悉C51版本Keil开发环境的使用 二、准备工作 Windows 操作系统Keil C51 安装包(可以从Keil官网下载)一款8051单片机开发板 三、搭建流程 环境搭建的基本流程…...

Ansible——playbook编写
目录 环境配置 一、简介 1.什么是playbook 2.playbook组成 二、应用实例 1.基础命令 1.编写 ceshi1.yaml 文件 2.运行Playbook 2.定义、引用变量 1.编写ceshi2.yaml文件 3.指定远程主机sudo切换用户 1.编写ceshi3.yaml文件 2.修改被控主机sudoers文件 3.给zhangsa…...

95、动态规划-编辑距离
递归暴力解法 递归方法的基本思想是考虑最后一个字符的操作,然后根据这些操作递归处理子问题。 递归函数定义:定义一个递归函数 minDistance(i, j),表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最小操作数。 递归终止条件…...

linux调试
文章目录 1. 使用打印来调试1.1 重定向1.2 标准预定义宏1.3 日志代码 2. 内核异常2.1 内核打印2.1.1 打印级别2.1.2 跟踪异常2.1.3 动态打印2.1.4 RAM console 2.2 OOPS2.2.1 有源代码的情况2.2.2 没有源代码的情况 3 查看日志4 工具调试 1. 使用打印来调试 1.1 重定向 2>…...

【C++】string类的使用②(容量接口Capacity || 元素获取Element access)
🔥个人主页: Forcible Bug Maker 🔥专栏: STL || C 目录 前言🔥容量接口(Capacity)size和lengthcapacitymax_sizereserveresizeclearemptyshrink_to_fit 🔥元素获取(Ele…...

【漏洞复现】某小日子太阳能系统DataCube3审计
漏洞描述 某小日子太阳能系统DataCube3终端测量系统 多个漏洞利用方式 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进…...

探索Java的未来
目录 一、云计算与大数据 二、人工智能与机器学习 三、物联网与边缘计算 四、安全性与性能优化 五、社区与生态 Java,作为一种广泛使用的编程语言,自其诞生以来就以其跨平台性、面向对象特性和丰富的库资源赢得了开发者的青睐。然而,随着…...

Web3 ETF软件开发
开发Web3 ETF软件涉及到金融、法律和技术等多个领域的专业知识,因此存在以下技术难点,开发Web3 ETF软件是一项复杂的技术挑战,需要综合考虑各种因素。开发人员需要具备较强的技术能力和跨学科知识才能成功开发Web3 ETF软件。北京木奇移动技术…...
初始MySQL
初始化MySQL数据库通常涉及以下步骤: 下载并安装MySQL: 你可以从MySQL官方网站下载适合你的操作系统的MySQL安装包。安装时,遵循安装向导的步骤,通常包括选择安装位置、选择组件(例如MySQL服务器、MySQL Workbench等&a…...
STM32项目下载清单(不定时更新)
收集的一些资料,分享下载 电赛一等奖作品,老人健康监测智能手表(STM32F4主控) STM32数字示波器源码数字信号处理教程、配套实例基于stm32 nucleo_L476的智能灯(操作说明源码)基于STM32 NUCLEO板设计彩色LE…...
thinkphp5 配合阿里直播实现直播功能流程
要为你提供一个更详细的教程来结合ThinkPHP 5和阿里直播SDK实现直播功能,需要涵盖的内容相对较多。不过,我可以为你提供一个大致的、更详细的步骤指南,供你参考和扩展: 1. 准备工作 a. 注册阿里云账号 前往阿里云官网注册账号&…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...