你要敢问我HashMap,那我绝对让你一脸懵逼 建议收藏

2024-05-02 01:18

本文主要是介绍你要敢问我HashMap,那我绝对让你一脸懵逼 建议收藏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • ①. hashCode方法返回值是int类型
  • ②. HashMap的综合效率是如何保证的-空间换时间
  • ③.HashMap算法
    • ①. h = hashCode ^ (hashCode >>> 16)的理解
    • ②. 为何不直接使用hashCode值来跟数组长度取模?
    • ③. 为什么index = h & (length-1) 要减去1?
  • ④. hashMap的二倍扩容
  • ⑤. 这有1000个非重复的键值对要存储,我需要怎么初始化我的HashMap?
  • ⑥. 高并发情况下扩容时的循环死链,Java8如何避免
  • ⑦. 什么是 hash,什么是hash表?

前言:
早年间,HashMap是面试场上必问的面试题,出去面试也会经常碰见。为了彰显个人学习的深度,以及领悟力。我们必须在面试的过程中,说点不一样的东西,像什么数组+链表的数据结构、Java8的链表树形化这种千篇一律的答案,已经没有任何新颖之处了,是非常平庸的答案。所以我们要说,就必须说点有意思的。在这个知识点上,必须把面试官拿下,接下来为了让大家从根源上认识HashMap,并且可以深入浅出的把它表达清楚,写出了如下的博客文章

①. hashCode方法返回值是int类型

  • ①. hashCode返回类型为int,意味着,不同的对象可能存在同样的hashCode值

  • ②. int取值范围是固定的,而对象是可以无穷多的,用有穷的数值去表示无穷的对象,那么肯定会存在重复
    在这里插入图片描述

②. HashMap的综合效率是如何保证的-空间换时间

  • ①. 数组寻址快,但是插入、删除慢。链表寻址慢,但是插入、删除快。而哈希表(散列),刚好介于二者之间,综合性能比较给力

  • ②. HashMap底层也是散列,通过key的hashCode值,经过一个计算(取模),可以迅速定位到数据的存储位置。但是,散列说到底,也是数组,只是一个空间比较富裕的数组而已,一个数据被存储到这个数组上,一般是用hashCode值对这个数组长度取模,得到数组下标的位置,从而快速定位。想要同一个位置冲突的几率小,那么只有增大空间,让离散度变大,这样效率才可保证!这也就是程序届有名的"空间换时间"的概念
    在这里插入图片描述

③.HashMap算法

  • ①. HashMap是如何存数、找数、寻找下标的——此算法必须领悟并记住,这就是资本
  1. 如果key为null,则默认对应的数组下标为0
  2. key的hashCode值记为hashCode
  3. 记h = hashCode ^ (hashCode >>> 16)
  4. 记数组长度len也就是HashMap容量cap
  5. index = h & (cap-1)
    在这里插入图片描述

①. h = hashCode ^ (hashCode >>> 16)的理解

  • ①. hashCode >>> 16:对hashCode值向右移16位,hashCode的值是一个32位的int,如果hashCode值比较小,那么高位就是0,相当于没有高位;如果hashCode的值比较大的话,高16就有数据

  • ②. hashCode ^ (hashCode >>> 16):这个时候,hashCode与高16位进行^运算。高16位参与运算,可以大大提高离散的程度。

②. 为何不直接使用hashCode值来跟数组长度取模?

  • ①. 如果直接使用hashCode和数组长度-1 进行运与运算,数组长度比较小的时候,这里我们使用数组长度为16。当hashCode很大的时候,高16位一直变动,而底16位处于不变的时候。因为数组长度16对应的后4位是1111。而我们hashCode低16位固定。固定的低16位和数组的后4位1111是一个固定的值,就是说元素储存的索引是固定的。元素都储存在同一个位置上,那产生的冲突将是非常恐怖的。意味着我们通过get( )去获取值,查询性能非常低,效率就也低

  • ②. 所以,这个算法,在进行后面取模求index之前,先获取了一个更具有代表这个数据整体的数值h(h = hashCode ^ (hashCode >>> 16))。这样操作之后就将hashCode值的高低16位都运用了起来,这样h & (cap - 1)取模的离散算法就更优了,产生的冲突的几率更小,访问效率更高

③. 为什么index = h & (length-1) 要减去1?

  • ①. 假如不减去1,length长度=16,这个时候和hashCode进行运与运算。16的后5位是10000,这个时候hashCode的后四位无论是什么结果都是4个0,而倒数第五位只有两个值,一个是0一个是1。如果是0那么对应储存的位置就是0;如果是1,对应储存的元素位置就是16。这个时候如果是0,那么所有的元素都储存在0位置,导致hash冲突,通过get()去查询元素时,效率极其低下。如果是1,对应储存的元素位置就是16,而索引最多只有15,所以会导致下标越界。

  • ②. 如果index = h & (length-1) ,假如这个时候length=16,我们的后4位就是1111,这个时候hashCode的后四位无论怎么变化,h & (length-1) 运算的结果都是在[0-15]之间的。

④. hashMap的二倍扩容

  • ①. 扩容核心,为何2倍扩容,为何默认初始化容量16?——新增一倍的空间,将原空间一半的数据移动到新空间,重新达到离散平衡、保证效率

二倍扩容说明:
二倍扩容、2的次幂——16初始化容量,实则都是为了完美整合寻址散列算法。
我们发现一个有趣的二进制计算技巧
n % m —> 当m的值是二的次幂的时候,它可以由 n & (m-1)替代,后者的效率也是更高。
比如:10 % 4 = 2 ==> 10 & 3 = 2
在这里插入图片描述
所以,如果默认初始化是16,那么数组最大下标用二进制表示就是1111。
那么当根据key获取到h值之后,h & 1111运算,获取到一个下标值,实则只跟h值的低四位有关。
关键点就在这里!当二倍扩容时!最大数组下标由二进制1111变成11111,四位变五位
那么h值二进制的第五位,它不是0,就是1!
如果是0,则下标值不变,因为0 & 1 为 0;如果是1,则新下标值=原下标值+原容量大小值
这就是二倍扩容、二的次幂默认初始化大小(16)、寻址算法、扩容实现的核心所在!
在这里插入图片描述

⑤. 这有1000个非重复的键值对要存储,我需要怎么初始化我的HashMap?

  • ①. 然而,1000个数你用1024的空间来存储,你还怎么实现空间换时间?富余的空间太少,哈希冲突肯定很多,影响使用效率。再说,HashMap默认负载因子0.75,它也不会让你1024空间存够1000,在存储的数据size到达768的时候,这个HashMap已经执行自动扩容了!为了不让它自动扩容,所以我们初始化为2048!当然你可以初始化为1023——2048之间的任何容量值,结果都是2048

解释:
1.如果我们确切的知道我们有多少键值对需要要存储(其它的动态数据结构使用亦是如此),那么我们在初始化HashMap的时候就应该指定它的容量,以防止HashMap自动扩容,影响使用效率。
2.有的同学说应该给它初始化容量为1000,有的同学说应该给他初始化为1024。实则都不对!
3.HashMap底层是这样实现的初始化的,当你给HashMap初始化为一个n的容量时,程序会用算法自动计算出一个不小于n的一个二的次幂值(原因前面已经说明),所以这里你就算给他指定1000容量,实则初始化后分配的容量也是1024。
在这里插入图片描述如上代码,实则是将cap这个数的二进制数从第一个1开始,把后面的二进制位全部变成1,然后+1操作,实则是取不小于n的一个最近的2的次幂

⑥. 高并发情况下扩容时的循环死链,Java8如何避免

  • ①. HashMap本身就是线程不安全的,所以这种问题(循环死链)是在错误的使用方法上出现的更为严峻的问题

  • ②. 在并发扩容的情况下,Jdk7是遍历每一个数组下标位置的链表,然后逐个元素采用头插法添加到新的空间。这样操作,在并发的情况下就会出现循环死链!

  • ③. 图解循环死链:
    在这里插入图片描述
    在这里插入图片描述

  • ④. Jdk8采用方法内部局部变量维护这个将被移动的数据(可能是一个新链表),然后在遍历结束后统一将新链表转移至对应新的下标位置,并非Jdk7那样一个一个元素进行转移。Jdk8链表添加元素采用尾插法

⑦. 什么是 hash,什么是hash表?

1.什么是hash? 它是将一个任2意长度的二进制值通过一个映射关系转换成一个固定长度的二进制值(1).任意长度的二进制值(2).映射关系[哈希算法--就相当于一个大学里面的学号的一个映射规则](3).固定的二进制值 [哈希值--就相当于大学里面的学号]任意长度的二进制值和固定长度的二进制的值 是一个一一对应的关系固定长度的二进制值就相当于一个关键字key真正有小的数据,就是这个学院的基本信息,一个任意长度的二进制值 value hash知识确定了一个key和一个value的唯一映射关系
2.hash表(1).特点:最重要的特点---它的储存效率很高,取数据的时间复杂度是1 o(1)(2).hash表 是通过一个key一个输入,通过一个哈希函数,找到数组中与这个key唯一映射的value通过这个hash函数,找到数组中这个value的下标,找到这个value[重要这句话]table aa=[];int index=hash(key);int value=aa[index];  注意:如果我们要找8,我们先写一个for循环,8和数组中的值进行比较。这样它的时间复杂度是n
3.hash函数key,找下标,有哪些方法可以找到下标?1.除留取余数法[就是%运算]int  index=key % m;m的取值规则是什么呢?m要取比数组长度小的最大质数m=15:int 1=1%15; 这个1就是下标  key=1,value=23 储存在index=1key=17 value=22 储存在index=2key=16 value=44 储存在index=1

这篇关于你要敢问我HashMap,那我绝对让你一脸懵逼 建议收藏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/953113

相关文章

为何我建议你学会抄代码?

文章目录 为何我建议你学会抄代码?一、引言二、抄代码的艺术1、理解抄代码的真正含义1.1、抄代码的好处 2、如何有效地抄代码2.1、发现问题2.2、整理需求2.3、造轮子标准流程 三、抄代码的实践案例1、发现问题2、整理需求3、设计重试机制4、实现重试工具类5、使用重试工具类6、优化和扩展 四、总结 为何我建议你学会抄代码? 一、引言 在编程的世界中,“抄代码” 常被视为一

收藏:解决 pip install 出现 error: subprocess-exited-with-error 错误的方法

在使用 pip 安装 Python 包时,有时候会遇到 error: subprocess-exited-with-error 错误。这种错误通常是由于 setuptools 版本问题引起的。本文将介绍如何解决这一问题 当你使用 pip install 安装某个 Python 包时,如果 setuptools 版本过高或过低,可能会导致安装过程出错,并出现类似以下错误信息:error: subpr

HashMap中常用的函数

假设如下HashMap<String, Integer> map = new HashMap<>();获取value值1、返回key为a的valueget(a)2、返回key为a的value,若没有该key返回0getOrDefault(a,0)新增键值对1、新增键值对(a,1)put(a,1)2、如果key为a的键不存在,则存入键值对(a,1)putIfAbsent(a,1)3

hashmap的存值,各种遍历方法

package com.jefflee;import java.util.HashMap;import java.util.Iterator;import java.util.Map;public class HashmapTest {// 遍历Hashmap的四种方法public static void main(String[] args) {//hashmap可以存一个null,把

2024数学建模国赛选题建议+团队助攻资料(已更新完毕)

目录 一、题目特点和选题建议 二、模型选择 1、评价模型 2、预测模型 3、分类模型 4、优化模型 5、统计分析模型 三、white学长团队助攻资料 1、助攻代码 2、成品论文PDF版 3、成品论文word版 9月5日晚18:00就要公布题目了,根据历年竞赛题目,可以分析A/B/C/D/E题目大概的类型,提前了解题目特点,在选题上就不会浪费过多时间。下面总结了一下5个题目各

Vue组件文件夹结构建议

全局通用组件 位于src/components。 注意与业务组件区分,全局通用组件更强调基础性。类似于一个UI框架里的各种Input、Button、Tab,只是在此处是你自己封装的。 建议风格 文件夹命名使用PascalBase风格一个文件夹代表一个组件组件使用index.vue导出 示例 目录结构 - src- components- SvgIcon- index.vue 使用

【银河麒麟高级服务器操作系统实例】虚拟化平台系统服务中断现象分析及处理建议

服务器环境以及配置 【机型】虚机 处理器: Kunpeng-920 内存: 40G 【内核版本】 4.19.90-23.8.v2101.ky10.aarch64 【OS镜像版本】 银河麒麟操作系统 Kylin-Server-10-SP1-Release-Build20-20210518-arm64 【第三方软件】 智能运维系统、mysql数据集群 现象描述 环境描

AI产品经理:ai产品经理从零基础到精通,非常详细收藏我这一篇就够了

在互联网的浪潮中,AI人工智能领域无疑是最引人注目的风口。AI产品经理,作为这一领域的新兴岗位,以其高薪、低压力、无年龄限制等优势,吸引了众多互联网从业者的目光。随着GPT等AIGC工具的兴起,AI产品经理的市场需求日益增长。 AI产品经理需不需要懂算法?🤔‍‍‍ AI产品经理不必像算法工程师那样精通算法,但必须能够与算法工程师有效沟通,了解如何管理AI项目,协调项目资源。 成功转行AI产

(4)SVG-path中的椭圆弧A(绝对)或a(相对)

1、概念 表示经过起始点(即上一条命令的结束点),到结束点之间画一段椭圆弧 2、7个参数 rx,ry,x-axis-rotation,large-arc-flag,sweep-flag,x,y (1)和(2)rx,ry rx:椭圆的x轴半径(即水平半径) ry:椭圆的y轴半径(即垂直半径) 这两个参数好理解,就是椭圆的两条对称轴半径,相等即为圆 也可以写比例,写比例时默认用符合条件

火狐浏览器重置密码后收藏的标签密码等数据被清除

火狐浏览器重置密码后收藏的标签密码等数据被清除 最早接触火狐是因为当时开发前端页面,firebug是当时最好用的前端调试工具。 用了很多年,最近因为一次重置密码,把我10几年的收藏数据全都清空了。还无法找回… 现在大部分web应用都要求使用chrome,比如在线文档、在线的office等,可是我还一直坚持使用火狐浏览器。 只是因为当初的先入为主,一直还坚持使用火狐浏览器,这次的遭遇让我丢失10年