面试题之HashMap

embedded/2024/10/11 11:21:18/

目录

Jdk1.7到Jdk1.8 HashMap 发⽣了什么变化(底层)?

HashMap的Put⽅法

HashMap的扩容机制原理

1.7版本

1.8版本

HashMap扩容为什么是扩为两倍? 


Jdk1.7到Jdk1.8 HashMap 发⽣了什么变化(底层)?

        1. 1.7中底层是数组+链表,1.8中底层是数组+链表+红⿊树,加红⿊树的⽬的是提⾼HashMap插⼊和 查询整体效率
        2. 1.7中链表插⼊使⽤的是头插法,1.8中链表插⼊使⽤的是尾插法,因为1.8中插⼊key和value时需要 判断链表元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使⽤尾插法
        3. 1.7中哈希算法⽐较复杂,存在各种右移与异或运算,1.8中进⾏了简化,因为复杂的哈希算法的⽬的 就是提⾼散列性,来提供HashMap的整体效率,⽽1.8中新增了红⿊树,所以可以适当的简化哈希 算法,节省CPU资源

HashMap的Put⽅法

1. 根据Key通过哈希算法与与运算得出数组下标
2. 如果数组下标位置元素为空,则将key和value封装为Entry对象(JDK1.7中是Entry对象,JDK1.8中 是Node对象)并放⼊该位置
3. 如果数组下标位置元素不为空,则要分情况讨论:
         a.如果是JDK1.7,则先判断是否需要扩容,如果要扩容就进⾏扩容,如果不⽤扩容就⽣成Entry对象,并使⽤头插法添加到当前位置的链表中
         b.如果是JDK1.8,则会先判断当前位置上的Node的类型,看是红⿊树Node,还是链表Node
如果是红⿊树Node,则将key和value封装为⼀个红⿊树节点并添加到红⿊树中去,在这个
过程中会判断红⿊树中是否存在当前key,如果存在则更新value
         c,如果此位置上的Node对象是链表节点,则将key和value封装为⼀个链表Node并通过尾插
法插⼊到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会
判断是否存在当前key,如果存在则更新value,当遍历完链表后,将新链表Node插⼊到链
表中,插⼊到链表后,会看当前链表的节点个数,如果⼤于等于8,那么则会将该链表转成
红⿊树
        d.将key和value封装为Node插⼊到链表或红⿊树中后,再判断是否需要进⾏扩容,如果需要
就扩容,如果不需要就结束PUT⽅法

HashMap的扩容机制原理

1.7版本

1. 先⽣成新数组
2. 遍历⽼数组中的每个位置上的链表上的每个元素
3. 取每个元素的key,并基于新数组⻓度,计算出每个元素在新数组中的下标
4. 将元素添加到新数组中去
5. 所有元素转移完了之后,将新数组赋值给HashMap对象的table属性

1.8版本

1. 先⽣成新数组
2. 遍历⽼数组中的每个位置上的链表或红⿊树
3. 如果是链表,则直接将链表中的每个元素重新计算下标,并添加到新数组中去
4. 如果是红⿊树,则先遍历红⿊树,先计算出红⿊树中每个元素对应在新数组中的下标位置
        a. 统计每个下标位置的元素个数
        b. 如果该位置下的元素个数超过了8,则⽣成⼀个新的红⿊树,并将根节点的添加到新数组的对应 位置
        c. 如果该位置下的元素个数没有超过8,那么则⽣成⼀个链表,并将链表的头节点添加到新数组的 对应位置
5. 所有元素转移完了之后,将新数组赋值给HashMap对象的table属性

HashMap扩容为什么是扩为两倍? 

HashMap扩容通常选择为原来的两倍,这一策略主要是为了在性能和内存占用之间寻找一个平衡点。这种策略基于以下几个原因:1

  1. 性能优化:扩容时,将容量扩大2倍可以更均匀地重新分布已有的元素,从而减少哈希冲突的概率。这意味着在新的容量下,每个桶中的元素数量更少,查找时间更短,提高了HashMap的性能。
  2. 空间利用率:虽然扩容会占用额外的内存,但2倍的扩容策略相对来说是一个合理的权衡。它不仅避免了频繁的扩容操作,还可以保持较高的空间利用率。如果扩容因子过小,可能会导致频繁的扩容操作,浪费内存;如果扩容因子过大,可能导致桶中的元素数量过多,增加了查找时间。
  3. 位运算优化:当扩容因子为2的幂次方时,可以使用位运算来计算哈希值的桶位置,而不需要进行昂贵的模运算,从而提高了计算的效率。
  4. 历史原因:在HashMap的早期版本中,选择2倍扩容因子是因为计算机中的整数乘法和除法操作通常要比其他因子(如3、5等)更快和更高效。

综上所述,2倍扩容因子是一种在性能和空间利用之间取得平衡的策略,它在实际应用中表现良好,使得HashMap在大多数情况下都能够提供高效的性能和相对高的空间利用率。但值得注意的是,如果特定应用场景需要不同的权衡,可以通过自定义HashMap来选择不同的扩容因子。


http://www.ppmy.cn/embedded/59015.html

相关文章

sql monitoring 长SQL ASH AWR 都没有 未Commit or export to csv

Duration 4小时, Database Time 22.5, Session Inactive, 1.未Commit原因, 2.慢慢导出成csv文件? How is v$session status INACTIVE and v$sql_monitor status EXECUTING concurrently 2641811 Posts: 8 Jan 11, 2016 6:47P…

WGCLOUD登录页面支持输入验证码吗

支持的 v3.5.3版本开始,WGCLOUD支持在登录页面配置输入验证码,我们可以根据自己的场景需要,配置是否在登录页面显示验证码,如下说明 登录页面添加验证码说明 - WGCLOUD

ubuntu 上vscode +cmake的debug调试配置方法

在ubuntu配置pcl点云库以及opencv库的时候,需要在CMakeLists.txt中加入相应的代码。配置完成后,无法调试,与在windows上体验vs studio差别有点大。 找了好多调试debug配置方法,最终能用的有几种,但是有一种特别好用&a…

Navicat Premium 15 for Mac/Win 中文安装包下载

Navicat Premium 15 是一款数据库管理工具,它支持多种类型的数据库,包括 MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL 和 SQLite。该软件提供了一个用户友好的图形界面,使得数据库的管理变得更加简单和高效。Navicat Premium 1…

13 - matlab m_map地学绘图工具基础函数 - 介绍创建管理颜色映射的函数m_colmap和轮廓图绘制颜色条的函数m_contfbar

13 - matlab m_map地学绘图工具基础函数 - 介绍创建管理颜色映射的函数m_colmap和轮廓图绘制颜色条的函数m_contfbar 0. 引言1. 关于m_colmap2. 关于m_contfbar3. 结语 0. 引言 本篇介绍下m_map中用于创建和管理颜色映射函数(m_colmap)和 为轮廓图绘制颜…

【STM32 ARM】寄存器操作GPIO的方法

文章目录 前言GPIO的简介GPIO 通过寄存器操作io_mux操作数据寄存器操作寄存器的原则简单操作寄存器高效处理寄存器 总结 前言 在嵌入式系统开发中,GPIO(General Purpose Input/Output,通用输入输出)是一种非常常见的硬件接口&…

阿里云通义千问开源两款语音基座模型分别是SenseVoice和CosyVoice

阿里巴巴近期发布了开源语音大模型项目FunAudioLLM,该项目包含了两个核心模型:SenseVoice和CosyVoice。可以精准多语言识别并且进行语音克隆。 SenseVoice:精准多语言识别与情感辨识 SenseVoice主要致力于高精度多语言语音识别、情感辨识和…

[vite] Pre-transform error: Cannot find package pnpm路径过长导致运行报错

下了套vue3的代码,执行pnpm install初始化,使用vite启动,启动后访问就会报错 报错信息 ERROR 16:40:53 [vite] Pre-transform error: Cannot find package E:\work\VSCodeProjectWork\jeecg\xxxxxxxxx-next\xxxxxxxxx-next-jeecgBoot-vue3\…