学习笔记044——HashMap源码学习2

文章目录

  • 1、HasMap 底层实现
  • 2、HashMap 加载顺序

1、HasMap 底层实现

JDK 1.8

HashMap 底层设计涉及到三种不同的数据结构,分别是数组、链表、红黑树。

1、基本的存储是数组,根据 key 值求出一个数组下标,将元素(key-value)存入到下标对应的位置。

在这里插入图片描述

2、存入的数据过多的话,key 所求出下标就有可能重复,叫做哈希冲突,怎么解决?引入链表,在数组的具体位置发展出一条链表,来存储多个下标一致的元素。

在这里插入图片描述

3、如果链表很长的话,会影响到元素的查询速度,为了提高查询速度,引入了红黑树(平衡二叉树),当链表长度大于 8 的时候,自动转换成红黑树。

在这里插入图片描述

2、HashMap 加载顺序

创建 HashMap 并不会创建数组,而是定义加载因子(数组扩容使用)

当给 HashMap 添加元素的时候,再来创建数组,懒加载机制,数组长度为 16。

(h = key.hashCode()) ^ (h >>> 16)

如果 key 不为 null,则返回 key 的 hashCode 和自己的高 16 位进行异或(相同为0,不同为1)运算得出的结果。

h >>> 16,无符号右移,取出 h 的高 16 位
0000 0100 1011 0011 1101 1111 1110 0001>>> 160000 0000 0000 0000 0000 0100 1011 0011

问题:既然已经拿到了 key 的 hashCode,为什么不直接返回?而要进行复杂的运算呢?

int 的取值范围是 -21 亿- 21 亿,40 亿种可能,而数组的默认长度为 16,远远小于 hash 可取值的范围,所以很有可能多次求出的下标根本不能用。

所以需要 hashCode 与自己的高 16 位进行异或运算,目的是混合原始 hash 码的高位和低位,以此来加大低位的随机性,尽量让值变小。

尽管如此,取到的值仍然很可能超过数组下标,所以为了保证绝对的安全性,拿到 hash 值之后,再与数组的最大索引(15)进行按位与运算(都为1返回1,否则返回0)得到索引。

hashCode:1111 1111 1111 1111 1111 0000 1110 1010h:			1111 1111 1111 1111 1111 0000 1110 1010
h >>> 16:	0000 0000 0000 0000 1111 1111 1111 1111hash:h^h>>>16:   1111 1111 1111 1111 0000 1111 0001 010115 & hash:		  0000 0000 0000 0000 0000 0000 0000 11111111 1111 1111 1111 0000 1111 0001 01010000 0000 0000 0000 0000 0000 0000 0101 = 5

HashMap 的存值过程

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//创建一个 Node 数组 tab,就是哈希桶Node<K,V>[] tab; Node<K,V> p; int n, i;//判断数组是否为空,如果为空就创建数组//HashMap的数组是延迟创建,这也是一种优化机制if ((tab = table) == null || (n = tab.length) == 0)//resize是创建数组和扩容数组的方法,这里是创建了长度为16的数组,用n记录它的长度,n=16n = (tab = resize()).length;//通过索引取出数组元素,判断是否为nullif ((p = tab[i = (n - 1) & hash]) == null)//如果为null,直接创建一个 Node 存入tab[i] = newNode(hash, key, value, null);else {//如果不为null,判断 key 是否相同Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))//如果相同,直接覆盖原来的值e = p;//如果不相同,需要在原 Node 的基础上那个添加新的 Node//首先判断该位置是链表还是红黑树else if (p instanceof TreeNode)//如果是红黑树,将 Node 存入红黑树e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {//如果是链表,就遍历链表for (int binCount = 0; ; ++binCount) {//找到最后一位if ((e = p.next) == null) {//将 Node 添加到链表的最后一位p.next = newNode(hash, key, value, null);//如果长度超过 8,需要将链表转成红黑树if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);//进入 treeifyBin 方法<!-- start -->if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)resize();else if ((e = tab[index = (n - 1) & hash]) != null) {<!-- end -->//当数组的长度小于 64 的时候,并不会转换成红黑树,而是对数组进行扩容,能用数组就尽量用数组,因为数组查询效率最高break;}//如果链表中存值重复的 key 值,直接把当前元素赋给 p 进行替换if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);//返回被覆盖的 valuereturn oldValue;}}++modCount;//Node 个数++ //threshold=capacity* loadFactor=16*0.75=12if (++size > threshold)resize();afterNodeInsertion(evict);//如果 key 不重复,则返回 nullreturn null;
}

什么是 Node?

Node 是 Map.Entry 的实现类,就将 key 和 value 封装成一个对象。

1、根据 key 计算 hash 值。
2、在 put 的时候判断数组是否存在,如果不存在用 resize 方法创建长度为 16 的数组。
3、确定要存入的 node 在数组中的位置,根据 hash 与数组最大索引进行按位与运算得到下标。
4、判断该位置是否有元素,如果没有直接创建一个 Node 存入。
5、如果有元素,判断 key 是否相同,如果相同,则覆盖并返回。
6、如果不同,需要在原 Node 基础上添加新的 Node,首先判断该位置是链表还是红黑树。
7、如果是红黑树,将 Node 存入红黑树。
8、如果是链表,遍历链表,找到最后一位将 Node 存入。
9、将 Node 存入之后,需要判断此时链表的长度是否超过 8,如果超过 8 需要将链表转换为红黑树,这里还有一个条件,如果数组的长度小于 64,会再进行一次数组扩容,当数组长度大于 64 的时候才会进行转换。
10、添加完成之后,再判断数组是否需要扩容。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络常见面试题总结(上)

计算机网络基础 网络分层模型 OSI 七层模型是什么&#xff1f;每一层的作用是什么&#xff1f; OSI 七层模型 是国际标准化组织提出的一个网络分层模型&#xff0c;其大体结构以及每一层提供的功能如下图所示&#xff1a; 每一层都专注做一件事情&#xff0c;并且每一层都需…

用micropython 操作stm32f4单片机的定时器实现蜂鸣器驱动

import pyb import time # 初始化引脚和定时器通道作为PWM输出 # 注意&#xff1a;这里我们假设您使用的是支持PWM的引脚和定时器 # 在不同的MicroPython板上&#xff0c;支持的引脚和定时器可能不同 # 请查阅您的板的文档以确认正确的引脚和定时器 buzzer_pin pyb.Pin(PD15,…

前端框架Vue3项目实战(基于Vue3实现一个小相册)

下面是是对Vue3操作的一个项目实战 下面代码是html的基本骨架&#xff08;没有任何的功能&#xff09;&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>相册</title> <style&…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-39

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

群控系统服务端开发模式-应用开发-前端短信配置开发

一、添加视图 在根目录下src文件夹下views文件夹下param文件夹下sms文件夹下&#xff0c;新建index.vue&#xff0c;代码如下 <template><div class"app-container"><div class"filter-container" style"float:left;"><el…

极致性能:19个Vue 项目的优化手段

前言 在前端开发领域&#xff0c;Vue.js 广泛应用于各种类型的项目中。然而&#xff0c;随着项目规模的扩大和用户需求的增加&#xff0c;性能优化的重要性愈发凸显。优化不仅可以提升用户体验&#xff0c;还能显著减少资源消耗&#xff0c;提高应用的响应速度和稳定性。 本文…

基于Java Springboot个人记账之财来财往微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

【maven-5】Maven 项目构建的生命周期:深入理解与应用

1. 生命周期是什么 ​在Maven出现之前&#xff0c;项目构建的生命周期就已经存在&#xff0c;软件开发人员每天都在对项目进行清理&#xff0c;编译&#xff0c;测试及部署。虽然大家都在不停地做构建工作&#xff0c;但公司和公司间&#xff0c;项目和项目间&#xff0c;往往…

LLamafactory API部署与使用异步方式 API 调用优化大模型推理效率

文章目录 背景介绍第三方大模型API 介绍LLamafactory 部署API大模型 API 调用工具类项目开源 背景介绍 第三方大模型API 目前&#xff0c;市面上有许多第三方大模型 API 服务提供商&#xff0c;通过 API 接口向用户提供多样化的服务。这些平台不仅能提供更多类别和类型的模型…

【Python网络爬虫笔记】6- 网络爬虫中的Requests库

一、概述 Requests 是一个用 Python 语言编写的、简洁且功能强大的 HTTP 库。它允许开发者方便地发送各种 HTTP 请求&#xff0c;如 GET、POST、PUT、DELETE 等&#xff0c;并且可以轻松地处理请求的响应。这个库在 Python 生态系统中被广泛使用&#xff0c;无论是简单的网页数…

【AI技术赋能有限元分析应用实践】Abaqus有限元分析到深度学习方法应用全过程——汽车刹车片热力耦合分析

目录 一、项目实现介绍**项目背景****项目目标****项目流程概述****技术融合****项目价值** 二、实现流程**Step 1: 分析问题构建方法&#xff0c;寻找主要分析目标&#xff0c;确定初步目标****Step 2: 使用 Abaqus 完成有限元仿真&#xff0c;后处理并保存数据为 odb 格式***…

【人工智能-科普】深度森林:传统机器学习与深度学习的创新结合

文章目录 深度森林:传统机器学习与深度学习的创新结合一、什么是深度森林?二、深度森林的工作原理1. **特征提取和转换**2. **多层级训练**3. **最终分类**三、深度森林的关键组成部分1. **森林层(Forest Layer)**2. **级联结构(Cascade Structure)**3. **特征增强(Feat…

Netty的内存池机制怎样设计的?

大家好&#xff0c;我是锋哥。今天分享关于【Netty的内存池机制怎样设计的&#xff1f;】面试题。希望对大家有帮助&#xff1b; Netty的内存池机制怎样设计的&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Netty 的内存池机制设计是为了提高性能&…

Postman设置接口关联,实现参数化

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 postman设置接口关联 在实际的接口测试中&#xff0c;后一个接口经常需要用到前一个接口返回的结果&#xff0c; 从而让后一个接口能正常执行&#xff0c;这…

七牛云成功保存但无法显示和访问{“error“:“download token not specified“}

在使用七牛云存储图片时&#xff0c;前端通过链接访问图片时遇到错误&#xff1a; {"error":"download token not specified"} 具体表现为&#xff1a; 后端通过 access_key 和 secret_key 生成了上传和下载的 Token。前端将域名与 res.key 拼接后生成图…

《实战OpenCV系列》专栏介绍

简介 本专栏由浅入深&#xff0c;详细介绍了使用OpenCV进行图像/视频处理的各方面知识&#xff0c;包括&#xff1a;图像显示、图像的数学运算、图像的裁剪与拼接、图像的像素操作、几何变换、直方图、图像滤波、色彩空间转换、边缘检测、形态学操作、模板匹配、视频处理、图像…

常用函数的使用错题汇总

#include <iostream> #include <fstream> #include <string>int main() {std::ifstream fin("example.txt"); // 创建 ifstream 对象并打开文件// 检查文件是否成功打开if (!fin) {std::cerr << "Error opening file!" << s…

曲面单值化定理

曲面单值化定理&#xff08;Uniformization Theorem&#xff09;是复分析、几何和拓扑学中的一个重要结果。它为紧致黎曼曲面提供了标准化的几何结构&#xff0c;是研究复几何和代数几何的基础。以下是对曲面单值化定理的详细介绍以及其应用场景。 曲面单值化定理的陈述 基本版…

【初阶数据结构和算法】二叉树顺序结构---堆的定义与实现(附源码)

文章目录 一、堆的定义与结构二、堆的实现1.堆的初始化和销毁堆的初始化堆的销毁 2.向上调整算法和入堆向上调整算法入堆 3.向下调整算法和出堆顶数据向下调整算法出堆 4.堆的有效数据个数和判空堆的有效数据个数堆的判空 5.取堆顶数据 三、堆的源码 一、堆的定义与结构 本篇内…

进程的知识

1. 冯诺依曼体系结构 输入设备&#xff1a;键盘&#xff0c;话筒&#xff0c;网卡&#xff0c;磁盘&#xff08;外存&#xff09; 外设&#xff1a; 输出设备&#xff1a;显示器&#xff0c;磁盘&#xff0c;网卡&#xff0c;打印机 CPU运算器控制器 存储器&#xff1a…