【HashMap】结构和底层原理

news2025/1/10 1:27:44

文章目录

    • 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 有较大的不同,不过并发度比前者好太多了

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

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

相关文章

模型的权值平均的原理和Pytorch的实现

一、前言 模型权值平均是一种用于改善深度神经网络泛化性能的技术。通过对训练过程中不同时间步的模型权值进行平均&#xff0c;可以得到更宽的极值点&#xff08;optima&#xff09;并提高模型的泛化能力。 在PyTorch中&#xff0c;官方提供了实现模型权值平均的方法。 这里…

李沐-《动手学深度学习》--02-目标检测

一 、目标检测算法 1. R-CNN a . 算法步骤 使用启发式搜索算法来选择锚框&#xff08;选出多个锚框大小可能不一&#xff0c;需要使用Rol pooling&#xff09;使用预训练好的模型&#xff08;去掉分类层&#xff09;对每个锚框进行特征抽取&#xff08;如VGG,AlexNet…)训练…

MYSQL篇--事务机制高频面试题

事务 1 什么是数据库事务&#xff1f; 事务是一个不可分割的数据库操作序列&#xff0c;也是数据库并发控制的基本单位&#xff0c;其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作&#xff0c;要么都执行&#xff0c;要么都不执行。…

【sqlite3】sqlite3在linux下使用sqlitebrowser工具实现数据可视化

sqlite3在linux下使用sqlitebrowser工具实现数据可视化 1. ### install sqlitebrowser 1. ### install sqlitebrowser 安装指令 sudo apt-get install sqlitebrowser通过工具打开数据库 sqlitebrowser stereo.db打开效果

HTTPS详解及openssl简单使用

OpenSSL 中文手册 | OpenSSL 中文网 本文介绍https传输协议中涉及的概念&#xff0c;流程&#xff0c;算法&#xff0c;如何实现等相关内容。 HTTP传输过程 HTTP 之所以被 HTTPS 取代&#xff0c;最大的原因就是不安全&#xff0c;至于为什么不安全&#xff0c;看了下面这张图…

Linux第25步_在虚拟机中备份“ST官方的TF-A源码”

TF-A是ARM公司提供的&#xff0c;ST公司通过修改它&#xff0c;做了一个自己的TF-A代码。因为在后期开发中&#xff0c;若硬件被改变了&#xff0c;我们需要通过修改"ST官方的TF-A源码"就可以自己的TF-A代码了。为了防止源文件被误改了&#xff0c;我们需要将"S…

亲测,Chatgpt4.0充值(虚拟卡充值)

一、准备工作&#xff1a; 1、一个ChatGPT3.5账号 2、一张支持ChatGPT4.0的虚拟卡 二、流程【网页版充值】 充值前请先确认以下三点&#xff1a; 1&#xff0c;ChatGPT账户正常登陆。 2&#xff0c;充值过程中始终保持美区环境&#xff0c;且开启全局模式。 3&#xff0…

简洁计算器Python代码

简洁的Python计算器&#xff0c;直接上代码&#xff08;用时10分钟&#xff09;&#xff1a; Python Gui图形化开发探索GUI开发的无限可能&#xff0c;使用强大的PyQt5、默认的Tkinter和跨平台的Kivy等工具&#xff0c;让Python成为你构建应用程序的得力助手。从本机用户界面到…

在 WinForms 应用程序中实现 FTP 文件操作及模式介绍

在 WinForms 应用程序中实现 FTP 文件操作及模式介绍 简介 在许多应用程序中&#xff0c;能够从远程服务器获取文件是一个非常有用的功能。本文将详细介绍如何在 Windows Forms (WinForms) 应用程序中使用 FTP 协议进行文件操作&#xff0c;包括连接到 FTP 服务器、列出目录、…

邂逅Node.JS的那一夜

邂逅Node.JS的那一夜&#x1f303; 本篇文章&#xff0c;学习记录于&#xff1a;尚硅谷&#x1f3a2; 本篇文章&#xff0c;并不完全适合小白&#xff0c;需要有一定的HTML、CSS、JS、HTTP、Web等知识及基础学习&#xff1a; &#x1f197;&#xff0c;紧接上文&#xff0c;…

通过反射修改MultipartFile类文件名

1、背景 项目上有这样一个需求&#xff0c;前端传文件过来&#xff0c;后端接收后按照特定格式对文件进行重命名。(修改文件名需求其实也可以在前端处理的) //接口类似于下面这个样子 PosMapping("/uploadFile") public R uploadFile(List<MultipartFile> fil…

Spring Boot注解大全:从入门到精通,轻松掌握Spring Boot核心注解!

目录 1、前言 2、介绍 2.1 Spring Boot简介 2.2 为什么要学习Spring Boot注解 3、Spring Boot基本注解 3.1 SpringBootApplication 3.2 EnableAutoConfiguration 3.3 ComponentScan 4、控制器注解 4.1 RestController 4.2 RequestMapping 4.3 PathVariable 4.4 Re…

主播风格的多样性

主播风格是主播在直播过程中表现出来的一种个性特点&#xff0c;它可以影响观众的感知和互动体验。以下是常见的几种主播风格: 1.时尚型:这种风格的主播通常穿着时尚、前卫&#xff0c;以潮流、新颖的形象出现在观众面前&#xff0c;善于捕捉时尚元素&#xff0c;并能够将其融…

JAVA销售数据决策管理系统源码

JAVA销售数据决策管理系统源码 基于BS&#xff08;Extjs Strus2springhibernate Mysql&#xff09;的销售数据的决策支持 主要的功能有 系统功能具体内容包括基础资料、进货管理、出货管理、库存管理、决策分析、系统管理。

基于书生·浦语大模型应用开发范式介绍

文章目录 大模型应用开发范式LangChain简介构建向量数据库搭建知识库助手RAG方案优化建议 大模型应用开发范式 通用大模型的优势&#xff1a; 强大的语言理解、指令跟随、语言生成的能力可以理解用户自然语言的指令具有强大的知识储备和一定的逻辑推理能力。 通用大模型局限…

springboot私人健身与教练预约管理系统源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

中国智造闪耀CES | 木牛科技在美国CES展亮相多领域毫米波雷达尖端方案

素有全球科技潮流“风向标”之称的2024国际消费类电子产品展&#xff08;CES&#xff09;&#xff0c;于1月9-12日在美国拉斯维加斯会议中心举办。CES是全球最大的消费电子和消费技术展览会之一&#xff0c;汇集了世界各地优秀的消费电子和科技公司&#xff0c;带着最好的产品来…

vue3中ref和reactive联系与区别以及如何选择

vue3中ref和reactive区别与联系 区别 1、ref既可定义基本数据类型&#xff0c;也可以定义引用数据类型&#xff0c;reactive只能定义应用数据类型 2、ref在js中取响应值需要使用 .value&#xff0c;而reactive则直接取用既可 3、ref定义的对象通过.value重新分配新对象时依旧…

Windows下上帝模式的实现

在windows系统上有个特殊模式&#xff0c;那就是上帝模式&#xff0c;几乎包含了windows中所有的快捷方式&#xff0c;有很多小伙伴还不知道&#xff0c;让我们一起来实现这一操作吧&#xff01; 一、首先新建一个文件夹 二、接着将文件夹重命名&#xff0c;命名为以下代码&am…

【OpenCV学习笔记07】- 【彩蛋】实现轨迹条控制画笔颜色和笔刷半径,并可以正常绘画

彩蛋 实现轨迹条控制画笔颜色和笔刷半径&#xff0c;并可以正常绘画。 直接上彩蛋代码 示例代码&#xff1a; # 彩蛋&#xff0c;创建一个可以调节颜色和笔刷半径的轨迹栏&#xff0c;并且可以通过鼠标进行绘画 import numpy as np import cv2 as cv# 定义全局变量 # 如果 …