HashMap底层的实现原理(JDK8)

news2025/7/19 5:28:57

目录

    • 一、知识点回顾
    • 二、HashMap 的 put() 和 get() 的实现
      • 2.1 map.put(k, v) 实现原理
      • 2.2 map.get(k) 实现原理
    • 三、HashMap 的常见面试题
      • 3.1 为何随机增删、查询效率都很高?
      • 3.2 为什么放在 HashMap 集合 key 部分的元素需要重写 equals 方法?
      • 3.3 HashMap 的 key 为什么是无序的?
      • 3.4 HashMap 怎么保持不可重复?
      • 3.5 HashMap 是如何扩容的?
      • 3.4 HashMap 在 JDK7 和 JDK8 有什么不同?
      • 3.5 HashMap 的哈希碰撞
      • 3.6 HashMap 的 key 允许为 null 吗?

HashMap的底层是通过数组 + 单向链表/红黑树实现的。

一、知识点回顾

数组特点:

  • 存储区间是连续的,且占用内存严重,空间复杂度也很大,时间复杂度为 O(1)。
  • 优点: 随机读取效率很高,原因是数组是连续的(随机访问性强,查找速度快)。
  • 缺点: 插入和删除数据效率低,因插入数据,这个位置后面的数据在内存中要后移,且大小固定不易动态扩展。

链表特点:

  • 区间离散,占用内存宽松,空间复杂度小,时间复杂度 O(n)。
  • 优点: 插入删除速度快,内存利用率高,没有大小固定,扩展灵活。
  • 缺点: 不能随机查找,每次都是从第一个开始遍历(查询效率低)。

哈希表特点:

以上数组和链表,大家都知道各自优缺点。那么我们能不能把以上两种结合在一起使用,从而实现查询效率高和删除插入效率也高的数据结构呢?答案是可以滴,那就是哈希表可以满足,接下来我们一起复习下 HashMap 中的 put() 和 get() 方法实现原理。

二、HashMap 的 put() 和 get() 的实现

2.1 map.put(k, v) 实现原理

  • 第1步,首先将 k, v 封装到 Node 对象当中(节点)。

  • 第2步,它的底层会调用 K 的 hashCode() 方法得出 hash 值。

  • 第3步,通过哈希表函数/哈希算法,将 hash 值转换成数组的下标:

    • 下标位置上如果没有任何元素,就把 Node 添加到这个位置上;
    • 如果说下标对应的位置上有链表,就会拿着 k 和链表上每个节点的 k 进行 equals:
      • 如果所有的 equals 方法返回都是 false,那么这个新的节点将被添加到链表的末尾;
      • 如其中有一个 equals 返回了 true,那么这个节点的 value 将会被覆盖。

在这里插入图片描述

2.2 map.get(k) 实现原理

  • 第1步,先调用 k 的 hashCode() 方法得出哈希值,并通过哈希算法转换成数组的下标。
  • 第2步,通过上一步哈希算法转换成数组的下标之后,再通过数组下标快速定位到链表所在位置上。
    • 如果这个位置上什么都没有,则返回 null;
    • 如果这个位置上有单向链表,那么它就会拿着参数 k 和单向链表上的每一个节点的 k 进行 equals:
      • 如果所有 equals 方法都返回 false,则 get 方法返回 null;
      • 如果其中一个节点的 k 和参数 k 进行 equals 返回 true,那么此时该节点的 value 就是我们要找的 value 了,get 方法最终返回这个要找的 value。

在这里插入图片描述

三、HashMap 的常见面试题

3.1 为何随机增删、查询效率都很高?

增删是在链表上完成的,而查询主要是通过数组定位,然后扫描部分链表,所以效率高。

HashMap 集合的 key,会先后调用两个方法:hashCode() 和 equals() 方法,所以当对象充当 key 时,这两个方法都需要重写。

3.2 为什么放在 HashMap 集合 key 部分的元素需要重写 equals 方法?

因为 equals 默认比较的是两个对象的内存地址,如果想根据对象的属性来判断,则需要重写。

3.3 HashMap 的 key 为什么是无序的?

因为不一定挂到哪一个单向链表上,因此加入顺序和取出也不一样。

3.4 HashMap 怎么保持不可重复?

使用 equals 方法来保证 HashMap 的 key 不可重复。如果 key 重复的话,value 就会覆盖。存放在 HashMap 集合中的 key,其实就是存放在 HashSet 集合中,所以 HashSet 集合也需要重写 equals() 和 hashCode() 方法。

3.5 HashMap 是如何扩容的?

HashMap 集合的默认初始化容量为16,默认加载因子为 0.75,也就是说当 HashMap 集合底层数组的容量达到 75% 时,数组就开始扩容。HashMap 集合初始化容量是 2 的倍数,是为了达到散列均匀,提高 HashMap 集合的存取效率。

3.4 HashMap 在 JDK7 和 JDK8 有什么不同?

  1. new HashMap<>(),底层不会再创建一个长度为 16 的数组,改为首次调用 put() 方法时创建;

  2. jdk8 底层的数组是 Node[],而非 Entry[]

  3. jdk7 底层结构只有:数组+链表,jdk8 中底层结构:数组+链表+红黑树

    3.1 形成链表时,七上八下

    ​ jdk7:头插法,新元素指向旧元素(多线程修改会有死锁问题);

    ​ jdk8:尾插法,旧元素指向新元素;

    3.2 为什么要用红黑树:

    • 首先,正常场景下不会一下子产生特别多的 Hash 冲突,只有非常规的场景下才会出现 Hash 冲突,需要转化为红黑树结构。
    • 红黑树解决了超长链表查询效率低下的问题,但是小数据量的链表并不比红黑树的查询效率要低。
    • Hash 值如果足够随机,则在 Hash 表内按泊松分布,在负载因子 0.75 的情况下,长度超过 8 的链表出现概率时 0.00000006,选择 8 就是为了尽量降低树化的几率。

    3.3 树化的两个条件:(必须都满足)

    • 哈希单向链表中的元素数 > 8
    • 当前数组的长度 > 64

    3.4 退化链表的条件:(任何一个满足)

    • 红黑树上的节点数 < 6
    • remove 节点时,若 root、root.left、root.right、root.left.left 有任意一个为 null,也会退化为链表。

3.5 HashMap 的哈希碰撞

如果 key1 和 key2 的哈希值相同,就会存放到同一个单向链表上。

如果 key1 和 key2 的哈希值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生哈希碰撞

3.6 HashMap 的 key 允许为 null 吗?

允许

JDK8 中 HashMap 的 put() 方法:

public V put(K key, V value) {
    // 采用 hash(key) 来计算 key 的 hashCode 值
    return putVal(hash(key), key, value, false, true);
}

static final int hash(Object key) {
    int h;
    // 当 key 为 null 的时候,不走 hashCode() 方法
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

HashMap 中使用 hash() 方法来计算 key 的哈希值,当 key 为 null 时,直接令 key 的哈希值为0,不走 key.hashCode() 方法,所以即使为 null 也不会抛出空指针异常。

整理完毕,完结撒花~ 🌻





参考地址:

1.来复习一波,HashMap底层实现原理解析,https://baijiahao.baidu.com/s?id=1665667572592680093&wfr=spider&for=pc

2.java中的hashMap允许key为null的原因,https://blog.csdn.net/weixin_46984636/article/details/120606095

3.HashMap,https://blog.csdn.net/qq_39181839/article/details/109478003

4.HashMap,https://blog.csdn.net/weixin_42470732/article/details/124027786

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/395749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML DOM 元素

创建新的 HTML 元素在文档对象模型 (DOM) 中&#xff0c;每个节点都是一个对象。DOM 节点有三个重要的属性&#xff0c;分别是&#xff1a;nodeName : 节点的名称nodeValue &#xff1a;节点的值nodeType &#xff1a;节点的类型创建新的 HTML 元素如需向 HTML DOM 添加新元素&…

一款OutLook信息收集工具

OutLook 这是一款burp插件&#xff0c;用于Outlook用户信息收集&#xff0c;在已登录Outlook账号后&#xff0c;可以使用该 插件自动爬取所有联系人的信息 安装 在burp扩展面板加载jar即可 功能介绍 All Users 加载插件后&#xff0c;进入Outlook联系人面板&#xff0c;…

unity开发知识点小结03

物理关节 铰链关节 按照固定的轴进行旋转 弹簧关节 两物体之间加装弹簧 固定关节 两个物体相关联 射线检测 通过射线检测&#xff0c;我们可以实现用鼠标来移动物体&#xff0c;当我们用鼠标点击场景中的某一位置&#xff0c;摄像机就发出一条射线&#xff0c;并且通过…

zookeeper从安装到入门

文章目录什么是zookeeperzookeeper的安装启动zookeeper并检查是否安装完成zookeeper的一些操作指令zookeeper的JavaAPI建立连接创建节点查询节点修改节点删除节点事件监听分布式锁集群leader选举规则集群角色什么是zookeeper ZooKeeper是一个分布式的&#xff0c;开放源码的分…

Fastjson 1.2.24 命令执行漏洞复现-JNDI简单实现反弹shell

文章目录前言一、环境搭建二、漏洞复现准备三、漏洞复现四、不成功的原因&#xff08;排查&#xff09;&#xff1a;总结前言 网上文章千篇一律&#xff0c;导致很多人都只会一种方法&#xff0c;只要有一种办法就所有人跟着这个办法去做了&#xff0c;新建java文件&#xff0…

Azure Function App Deploy Issue

问题&#xff1a;之前一直用vs code 的 Azure Function extension 工具部署&#xff0c;没有什么问题。直到3月份发现 vs code显示部署成功&#xff0c;但是通过 https://<function_name>.scm.azurewebsites.net/DebugConsole 查看上传后的 dll 文件&#xff0c;dll文件根…

每日学术速递3.8

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Unleashing Text-to-Image Diffusion Models for Visual Perception 标题&#xff1a;释放用于视觉感知的文本到图像扩散模型 作者&#xff1a;Wenliang Zhao, Yongming Rao, Zuya…

【maven 学习记录】

maven 学习记录一、maven基础1. maven是什么2. maven的作用3. maven的下载安装4. maven仓库5. maven坐标6. 第一个maven项目 手工实现7. maven插件8. 依赖管理9. 生命周期二、maven进阶一、maven基础 1. maven是什么 maven的本质是一个项目管理工具&#xff0c;将项目开发和管…

mac电脑解决Error: command failed: npm install --loglevel error --legacy-peer-deps

使用vue create xxx创建vue3项目的时候报错。 解决步骤&#xff1a; 1.sudo npm cache clean --force 2.再次创建就可以成功 补充&#xff1a;网上搜到很多方法&#xff0c;都尝试失败&#xff0c;因为遇到需要打开.vuerc,.npmrc的情况&#xff0c;记录一下怎样找到文件 1. 尝…

xshell的复制ssh渠道和复制会话是什么意思

xshell的复制ssh渠道和复制会话是什么意思1、复制ssh渠道1.1、只打开一个标签页时1.2、复制ssh渠道后1.3、复制第2个ssh渠道后2、复制会话3、两个ssh进程分别复制ssh渠道4、总结1、复制ssh渠道 1.1、只打开一个标签页时 ssh进程有1个&#xff0c;pid是98959。用户是root、终端…

工业级64 位AM6231ASGGGAALW/AM6232ATCGGAALW【处理器】AM6234ATCGGAALW

工业级64 位AM6231ASGGGAALW/AM6232ATCGGAALW【处理器】AM6234ATCGGAALW说明&#xff1a;低成本的AM62x Sitara MPU系列应用处理器专为Linux 应用开发而设计。凭借可扩展的ArmCortex -A53性能和嵌入式功能&#xff0c;例如:双显示支持和3D图形加速&#xff0c;以及广泛的外围设…

Flutter-自定义图标

虽然Flutter有许多内置的icon图标&#xff0c;但是有些特殊功能的话&#xff0c;需要自定义图标或者需要在iconfont 阿里巴巴的图标库里找对应合适的图标。 第一步&#xff1a;在iconfont 阿里巴巴里搜索想要的图标并加入到购物车&#xff0c;点击下载代码后&#xff0c;会生成…

2023增加收入的最佳销售聊天机器人

哈佛大学的研究表明&#xff0c;快乐的客户花费更多。聊天机器人可以很大程度提高客人体验感&#xff0c;可以增加您的销售额。现在&#xff0c;快来get市场上最好的销售聊天机器人并增加您的收入&#xff01; 一、什么是销售聊天机器人&#xff1f; 销售聊天机器人是一种自动…

使用 Wall 教你搭建 照片墙 和 视频墙

下载 Github:https://github.com/super-tongyao/wall 国内仓库&#xff08;不推荐&#xff0c;只做加速访问&#xff0c;无编译包和发行版&#xff0c;以github仓库为准&#xff09;&#xff1a;https://gitee.com/Super_TongYao/wall 推荐github仓库&#xff0c;下载最新版…

演化算法:乌鸦搜索算法 (Crow Search Algorithm)

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 在机器学习中&#xff0c;我们所要优化的问题很多时候难以求导&#xff0c;因此通常会采用一些演化算法&#xff08;又称零…

C++——模板

文章目录1 泛型编程2 函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的实例化2.3.1 隐式实例化2.3.1.1 定义2.3.1.2 代码演示2.3.1.3 运行结果2.3.1.4 缺点2.3.2 显式实例化2.3.2.1 格式2.3.2.2 代码演示2.3.2.3 运行结果2.4 模板参数的匹配原则2.4.12.4.22.4.33 类模板…

并发编程的三大挑战之原子性及其解决方案

目录 一、原子性问题 1、带来原子性问题的原因 2、如何解决线程切换带来的原子问题 2.1、使用synchronized关键字来保证 2.2、使用CAS来保证原子性 2.3、使用lock锁来保证 一、原子性问题 1、带来原子性问题的原因 线程切换是带来原子的根本原因&#xff0c;java的并发程…

proteus光敏电阻电路的arduino仿真

虽然Fritzing0.9.10有了仿真的功能&#xff0c;但都是测试板&#xff0c;能够仿真的很有限&#xff0c;所以还是要借助proteus来仿真。这里&#xff0c;我们来实先一个简单的光明电阻的仿真电路。本篇博文&#xff0c;重点演示proteus仿真arduino光敏电阻&#xff0c;arduino采…

Kerberos 域委派攻击之基于资源的约束性委派

CSDN自动迁移博客文章注意区别&#xff1a;约束性委派 不能跨域进行委派&#xff0c;基于资源的约束性委派可以跨域和林如果约束性委派&#xff0c;必须拥有 SeEnableDelegationPrivilege 权限&#xff0c;该特权是敏感的&#xff0c;通常仅授予域管理员。为了使用户/资源更加独…

DDD领域驱动设计初探

DDD 强调领域模型要兼顾业务和技术两个视角。 我们怎么用一套系统化的方法&#xff0c;抽丝剥茧、一步一步地把需求落实到代码呢&#xff1f;咱们看看下面这张图&#xff0c;它表示了领域驱动设计中的主要流程。 领域驱动设计主要的开发流程你可以看到&#xff0c;在整个开发流…