【HashMap】结构和底层原理

文章目录

    • HashMap
      • 结构和底层原理

HashMap

结构和底层原理

​ HashMap 是我们非常常用到数据结构,由数组和链表构成的数据结构,数组里面每个地方都存了 key-value 这样的实例,在Java7叫 Entry 在 Java8 中叫 Node

在这里插入图片描述

​ 因为他本身所有的位置都是null,在 put 插入的时候,会根据 key 的hash去计算一个 index 的值,就比如我 put(“Hello”,“World”),我插入了为“Hello”的元素,这个时候,我们会通过 哈希函数计算出插入的位置,计算出 index 是 1 的位置,

hash("Hello") = 1

在这里插入图片描述

​ 我们都知道,数组的长度是有限的,在有限的长度去使用哈希,哈希本身就存在概率性,就像上面的情况,可能为再 put 一个 “hi” ,计算出的 hash 值也在 1上,和之前是同一值,这样就形成了链表

在这里插入图片描述

每一个节点都会保存自身的hash、key、value、以及下个节点,我看看Node的源码。

static class Node<K,V> implement Map.Entry<K,V>{
  final int hash;
  final K key;
  V value;
  Node<K,V> next;
  ...
}

新的Entry节点再插入链表的时候,是怎么插入的?

Java 8之前是头插法,也就是说,新的值会取代原有的值,原有的值顺推到链表中去,就像上面的🌰一样

但是 Java 8 之后,都说用尾插法了。

为什么改成尾插法?

​ 数组容量是有限的,数据多次插入,到达一定数量就会扩容,也就是 resize

什么时候resize呢?

有两个因素

  • Capacity:HashMap 当前长度
  • LocalFactor:负载因子,默认值为 0.75f
static final float DEFAULT_LOAD_FACTOR = 0.75f;

比如当前容量为 100,当存到 76 个的时候,判断发现需要进行 resize 了,那就扩容。

怎么扩容呢?

分为两步:

  • 扩容:创建一个新的 Entry 空数组,长度是原来的2倍
  • ReHash:遍历原Entry 数组,把所有 Entry 重新 Hash 到新的数组

为什么要重新Hash呢?为什么不直接复制过去

​ 因为长度扩容之后,Hash 规则也随之改变,原来的长度是 8 是通过位运算得出的,这和新的 16 肯定就不一样了。

Hash的公式—> index = HashCode(Key) & (Length - 1)

扩容前:
在这里插入图片描述

扩容后:
在这里插入图片描述

言归正传,java8 为什么改成尾插了呢?

​ 多线程的情况下,头插法进行扩容。会出现环形链 –Infinit Loop(♾️)

# 插入 A、B、C到长度为2的Entry 中 当长度依旧还是2的时候,未扩容时
顺序是 A -> B -> C

# 2*0.75 = 1,插入到第二个就要 resize 了,因为 resize ,使用单链表头插法,同一个位置上的元素总会被放到链表头部,重新计算的索引位有可能被放到新的不同的位置
顺序是 B -> A

# 一旦多个线程完成调整
这时 ...  A -> B  -> A -> B ...

因为 Java 8 之后链表有红黑树,代码中有很多 if else 的逻辑判断,红黑树的引入将原本 O(n) 的时间复杂度 降低为 O(logn),使用头插法会改变链表上的顺序,但是如果用尾插法,在扩容的时候会保持链表元素原本的顺序,就不会出现链表成环的问题了

那是不是意味着 Java8 就可以把 HashMap 用在多线程呢?

​ 源码中的 put/get 方法都没有加同步锁,所以线程安全还是无法保证的。

HashMap 的默认初始化长度是多少?

​ 16

为什么是16呢?

​ 源码中有一行

static final int DEFAULT_INITIAL_CAPACITY = 1<<4;

1<<4 = 16 在创建 HashMap 时,阿里巴巴规会提示我们最好赋初值,而且最好是2的幂,这样是为了位运算方便,位与运算比算数计算的效率高了很多,之所以选择16,是为了服务将 key 映射到 index 的算法。

那为啥用16不用别的呢?

​ 这是为了实现均匀分布

为啥我们重写equals方法的时候需要重写hashCode方法呢?

​ 因为在java中,所有的对象都是继承于Object类。Ojbect类中有两个方法equals、hashCode,这两个方法都是用来比较两个对象是否相等的。在未重写equals方法我们是继承了object的equals方法,那里的 equals是比较两个对象的内存地址,显然我们new了2个对象内存地址肯定不一样

  • 对于值对象,==比较的是两个对象的值
  • 对于引用对象,比较的是两个对象的地址

我们通过 key 去计算出 index,一个index 下面可能会有n个元素,如果不重写 hashCode 获取到 准确的 hash值,根本找不到呀!所以针对 Map重写 equals 方法后,还需要重写 hashCode 方法。

HashMap在线程安全的场景

​ 在多线程情况下,一般都会使用HashTable 或者 CurrentHashMap,但是因为前者都是并发度的原因基本上没啥使用场景,所以存在线程不安全情况我们都是用 CurrentHashMap

​ HashTable源码我看过,很简单粗暴,直接在方法上锁,并发度很低,最多同时允许一个线程访问,currentHashMap就好很多,1.7 和 1.8 有较大的不同,不过并发度比前者好太多了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何查看一篇论文是期刊还是会议?

如何查看一篇论文是期刊还是会议&#xff1f;这是大家看论文的时候比较关注的事情&#xff0c;还有这篇论文是什么等级的&#xff1f; 1、如何查看一篇论文是期刊还是会议&#xff1f; 从论文的封面可以直接看出来&#xff0c;比如下面这种&#xff1a; conference就是会议的意…

【AI】AI和医疗大数据(1/3)

目录 一、医疗大数据有哪些 二、医疗大数据的特性 1. 隐私性 2. 复杂性 3. 不均衡性 4. 时序性 三、医疗大数据的目标和挑战 博主曾经在医疗智能设备领域创业&#xff0c;由于当时选择的模式过于复杂&#xff0c;包括了机械硬件、智能终端软硬件、院后微信生态做随访服务…

开启Android学习之旅-2-架构组件实现数据列表及添加(kotlin)

Android Jetpack 体验-官方codelab 1. 实现功能 使用 Jetpack 架构组件 Room、ViewModel 和 LiveData 设计应用&#xff1b;从sqlite获取、保存、删除数据&#xff1b;sqlite数据预填充功能&#xff1b;使用 RecyclerView 展示数据列表&#xff1b; 2. 使用架构组件 架构组…

Untiy HTC Vive VRTK 开发记录

目录 一.概述 二.功能实现 1.模型抓取 1&#xff09;基础抓取脚本 2&#xff09;抓取物体在手柄上的角度 2.模型放置区域高亮并吸附 1&#xff09;VRTK_SnapDropZone 2&#xff09;VRTK_PolicyList 3&#xff09;VRTK_SnapDropZone_UnityEvents 3.交互滑动条 4.交互旋…

cpp_10_多重继承_钻石继承_虚继承

1 多重继承 一个类可以同时从多个基类继承实现代码。 1.1 多重继承的内存布局 子类对象内部包含多个基类子对象。 按照继承表的顺序依次被构造&#xff0c;析构的顺序与构造严格相反。 各个基类子对象按照从低地址到高地址排列。 // miorder.cpp 多重继承&#xff1a;一个子…

Rust类型之字符串

字符串 Rust 中的字符串类型是String。虽然字符串只是比字符多了一个“串”字&#xff0c;但是在Rust中这两者的存储方式完全不一样&#xff0c;字符串不是字符的数组&#xff0c;String内部存储的是Unicode字符串的UTF8编码&#xff0c;而char直接存的是Unicode Scalar Value…

大模型学习之书生·浦语大模型4——基于Xtuner大模型微调实战

基于Xtuner大模型微调实战 Fintune简介 海量数据训练的base model指令微调Instructed LLM 增量预训练微调 增量数据不需要问题&#xff0c;只需要答案&#xff0c;只需要陈述类的数据 指令跟随微调 指定角色指定问题给对应的user指定答案给assistant LIaMa2InternLM 不同的模…

什么是Modbus协议?

Modbus协议是一种在工业自动化领域广泛应用的通信协议&#xff0c;它允许不同设备之间进行可靠的数据交换和控制。该协议最初由Modicon公司于1979年创建&#xff0c;旨在提供一种简单而有效的方法&#xff0c;使PLC&#xff08;可编程逻辑控制器&#xff09;和其他自动化设备能…

前端绕过无限Debug

1.准备 burp : https://pan.baidu.com/s/1aqCywnF_S-HzIWVGLjiW-A 提取码: mpen BurpLoaderKeygen:链接: https://pan.baidu.com/s/1Vck_hFMT2YXP1cbmYfFqsA 提取码: qggp 点击Next后把Request粘贴到LoaderKeygen中&#xff0c;然后把Response粘贴到Burp Suite中 注&#xff1…

2024年【熔化焊接与热切割】考试内容及熔化焊接与热切割免费试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 熔化焊接与热切割考试内容是安全生产模拟考试一点通总题库中生成的一套熔化焊接与热切割免费试题&#xff0c;安全生产模拟考试一点通上熔化焊接与热切割作业手机同步练习。2024年【熔化焊接与热切割】考试内容及熔化…

in <module> input = brower.find_element(‘kw‘)

1. 概念名称&#xff1a; in <module> input brower.find_element(kw) 2. 概念定义&#xff1a; 这行代码使用了Selenium WebDriver的find_element方法来定位页面上的一个元素 3. 我对概念的理解&#xff1a; find_element方法用于查找页面上的元素&#xff0c;但这里的…

Mysql是怎样运行的--下

文章目录 Mysql是怎样运行的--下查询优化explainoptimizer_trace InnoDB的Buffer Pool&#xff08;缓冲池&#xff09;Buffer Pool的存储结构空闲页存储--free链表脏页&#xff08;修改后的数据&#xff09;存储--flush链表 使用Buffer PoolLRU链表的管理 事务ACID事务的状态事…

Triumphcore FPGA调测试记录

FPGA采用Xilinx pynq Z2开发板。基于V2.5版本开发 OverView uart端口映射 BUG调试记录 2024.1.7 复位状态导致取指时序错误 错误波形&#xff1a; 正确波形 问题代码&#xff1a; 2024.1.9 clock_wizard设置输入时钟是输出时钟的2^n倍&#xff0c;输出时钟的占空比才…

电能质量Python实现全家桶——全网最低价

往期精彩内容&#xff1a; 电能质量扰动信号数据介绍与分类-Python实现-CSDN博客 Python电能质量扰动信号分类(一)基于LSTM模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(二)基于CNN模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(三)基于Transformer…

数据结构之单调栈、单调队列

今天学习了单调栈还有单调队列的概念和使用&#xff0c;接下来我将对其定义并配合几道习题进行讲解&#xff1a; 首先先来复习一下栈与队列&#xff1a; 然后我们来看一下单调栈的定义&#xff1a; 单调栈中的元素从栈底到栈顶的元素的大小是按照单调递增或者单调递减的关系进…

Spring之整合Mybatis底层源码

文章目录 一、整体核心思路1 . 简介2. 整合思路 二、源码分析1. 环境准备2. 源码分析 一、整体核心思路 1 . 简介 有很多框架需要与Spring进行整合&#xff0c;而整合的核心思路就是把其他框架所产生的对象放到Spring容器中&#xff0c;让其成为一个bean。比如Mybatis&#x…

使用requests库测试post请求 操作流程

第一步 谷歌f12或其他抓包工具抓包&#xff0c;这里随机抓一个post请求 url&#xff1a;https://eva2.csdn.net/v3/06981375190026432f77c01bfca33e32/lts/groups/dadde766-b087-42da-8e67-d2499a520ee7/streams/a0119567-bf91-4314-ab75-f683ba6c0c0a/logs 第二步 导包 impo…

uniapp在web端怎么使用svg图标呢

在图标库中添加好项目用到的图标&#xff0c;点击symbol点击生成在线链接 点击生成的在线链接&#xff0c;此时会跳转到一个新窗口&#xff0c;是一个js文件 复制这个js文件的内容 然后在uniapp中新建svg.js文件&#xff0c;把从上面复制的代码粘贴到这个svg.js中 在main.js中引…

在本地测试nginx中localhost不行,需要写成127.0.0.1

在Windows 10系统的命令提示符cmd中&#xff0c;执行命令ping localhost&#xff0c;并没有出现我与其的ip地址“127.0.0.1”&#xff0c;而是“[::1]”。 问题原因 在cmd中ping localhost解析出来的是ipv6的::1的原因是windows有个优先解析列表&#xff0c;当ipv6的优先级高于…

C++学习笔记——对象的指针

目录 一、对象的指针 二、减少对象的复制开销 三、应用案例 游戏引擎 图像处理库 数据库管理系统 航空航天软件 金融交易系统 四、代码的案例应用 一、对象的指针 是一种常用的技术&#xff0c;用于处理对象的动态分配和管理。使用对象的指针可以实现以下几个方面的功…