面试题分享之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. 注册阿里云账号 前往阿里云官网注册账号&…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...
李沐--动手学深度学习--GRU
1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...
