面试题分享之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. 注册阿里云账号 前往阿里云官网注册账号&…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
