HashMap -- 调研

HashMap 调研

      • 前言
        • JDK1.8之前
          • 拉链法:
        • JDK1.8之后
        • JDK1.7 VS JDK1.8 比较
          • 优化了一下问题:
      • HashMap的put方法的具体流程?
      • HashMap的扩容resize操作怎么实现的?

前言

在Java中,保存数据有两种比较简单的数据结构:数组和链表。

数组的特点是:寻址容易,插入和删除 困难;
链表的特点是:寻址困难,但插入和删除容易;

所以我们将数组和链表结合在一起,发挥两者各 自的优势,使用一种叫做拉链法的方式可以解决哈希冲突


JDK1.8之前

JDK1.8之前采用的是拉链法。

拉链法:
   将链表和数组相结合。也就是说创建一个链表数组,

数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。

在这里插入图片描述

JDK1.8之后

相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将
链表转化为红黑树,以减少搜索时间。

在这里插入图片描述


JDK1.7 VS JDK1.8 比较
优化了一下问题:
  1. resize 扩容优化
  2. 引入了红黑树,目的是避免单条链表过长而影响查询效率
  3. 解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。

HashMap的put方法的具体流程?

在这里插入图片描述

public class HashMapDemo<K, V> extends HashMap<K, V> {// 默认初始容量 - 必须是 2 的幂。static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16// 如果任一带有参数的构造函数隐式指定更高的值,则使用最大容量。必须是 2 的幂 <= 1<<30。static final int MAXIMUM_CAPACITY = 1 << 30;// 初始因子static final float DEFAULT_LOAD_FACTOR = 0.75f;// 使用树而不是箱列表的箱计数阈值。当将元素添加到至少具有这么多节点的 bin 时,bin 会转换为树。该值必须大于2,并且至少应为8,以便与树木移除中有关收缩后转换回普通箱的假设相吻合。static final int TREEIFY_THRESHOLD = 8;// 在调整大小操作期间对(分割)bin 进行树形化的 bin 计数阈值。应小于 TREEIFY_THRESHOLD,且最多 6 个网格,以便在移除时进行收缩检测。static final int UNTREEIFY_THRESHOLD = 6;// bin 可以树化的最小表容量。  初始数量(否则,如果 bin 中的节点太多,则表的大小将被调整。)应至少为 4的倍数以避免调整大小和树化阈值之间的冲突。static final int MIN_TREEIFY_CAPACITY = 64;transient HashMapDemo.Node<K, V>[] table;transient Set<Entry<K, V>> entrySet;transient int size;transient int modCount;int threshold;// 创建一个节点Node类  作为链表使用static class Node<K, V> implements Map.Entry<K, V> {// hash值final int hash;// keyfinal K key;// 对应的值V value;// 子节点HashMapDemo.Node<K, V> next;Node(int hash, K key, V value, HashMapDemo.Node<K, V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}// get setpublic final K getKey() {return key;}public final V getValue() {return value;}public final String toString() {return key + "=" + value;}// 计算规则,  节点上的key 进行hashCode 计算,异或 hashCode 值public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}public final V setValue(V newValue) {V oldValue = value;value = newValue;return oldValue;}public final boolean equals(Object o) {if (o == this)return true;if (o instanceof Map.Entry) {Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;if (Objects.equals(key, e.getKey()) &&Objects.equals(value, e.getValue()))return true;}return false;}}// 定义一个 hash 方法, 通过hashCode 得到一个长度 位运算 h>>> 高低16bitstatic final int hash(Object key) {int h;// key.hashCode()) ^ (h >>> 16)   hashcode  和 自己hashcode 位运算异或return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}// put方法@Overridepublic V put(K key, V value) {return super.put(key, value);}V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {HashMapDemo.Node<K, V>[] tab;HashMapDemo.Node<K, V> p;int n;int i;// 初始化表大小或将表大小加倍。如果为空,则根据阈值字段中保存的初始容量目标进行分配if ((tab = table) == null || (n = tab.length) == 0) {// 步骤1:tab为空则创建   table未初始化或者长度为0,进行扩容n = (tab = resize()).length;}// 计算下标是否为空  通过hash算法  得到 p (n - 1) & hash 确定元素存放在哪个桶中if ((p = tab[i = (n - 1) & hash]) == null) {// 为空找不到,放到桶里面tab[i] = newNode(hash, key, value, null);}// 桶里面存在类else {HashMapDemo.Node<K, V> e;K k;// 步骤3:节点key存在,直接覆盖value比较桶中第一个元素(数组中的结点)的hash值相等,key相等if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) {// 直接覆盖值e = p;}// 步骤4:判断该链为红黑树  hash值不相等,即key不相等;为红黑树结点 如果当前元素类型为TreeNode,表示为红黑树,putTreeVal返回待存放的node   e可能为空else if (p instanceof TreeNode) {// 放入树中e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);}// 步骤5 不是树就是链表else {// 循环for (int binCount = 0; ; ++binCount) {// 为空最后一个节点if ((e = p.next) == null) {// 在链表最末插入Node结点p.next = newNode(hash, key, value, null);判断链表的长度是否达到转化红黑树的临界值,临界值为8// TREEIFY_THRESHOLD 属性 8 前面定义类if (binCount >= TREEIFY_THRESHOLD - 1)// 链表结构转树形结构treeifyBin(tab, hash);break;}// 判断链表中结点的key值与插入的元素的key值是否相等if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {// 相等,跳出循环break;}// 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表p = e;}}// 判断当前的key已经存在的情况下,再来一个相同的hash值、key值时,返回新来的val ue这个值if (e != null) {// 记录e的valueV oldValue = e.value;// onlyIfAbsent为false或者旧值为nullif (!onlyIfAbsent || oldValue == null) {// 用新值替换旧值e.value = value;}// 访问后回调afterNodeAccess(e);// 返回旧值return oldValue;}}// 结构性修改++modCount;// 步骤6:超过最大容量就扩容 实际大小大于阈值则扩容if (++size > threshold) {// 插入后回调resize();}// node 节点插入afterNodeInsertion(evict);return null;}
}
  1. 判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;

  2. 根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向6,如果table[i]不为空,转向3;

  3. 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向4,这里的相同指的是hashCode以及equals;

  4. 判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值 对,否则转向5;

  5. 遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操 作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;

  6. 插入成功后,判断实际存在的键值对数量size是否超多了 大容量threshold,如果超过,进行扩容。


HashMap的扩容resize操作怎么实现的?

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

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

相关文章

【RabbitMQ 实战】11 队列的结构和惰性队列

一、 队列的结构 队列的组成&#xff1a; 队列由 rabbit_amgqueue_process 和 backing_queue两部分组成。rabbit_amqqueue_process负责协议相关的消息处理&#xff0c;即接收生产者发布的消息、向消费者交付消息、处理消息的确认 (包括生产端的 confirm 和消费端的 ack) 等。…

Spring Boot读取配置文件

Spring Boot 是一种用于快速构建基于Spring的应用程序的框架&#xff0c;它提供了很多便利的功能和约定&#xff0c;使开发者可以快速搭建、配置和部署应用程序。在Spring Boot中&#xff0c;读取配置文件是一个非常常见的任务&#xff0c;本文将介绍如何在Spring Boot应用程序…

Qt/C++原创推流工具/支持多种流媒体服务/ZLMediaKit/srs/mediamtx等

一、前言 1.1 功能特点 支持各种本地视频文件和网络视频文件。支持各种网络视频流&#xff0c;网络摄像头&#xff0c;协议包括rtsp、rtmp、http。支持将本地摄像头设备推流&#xff0c;可指定分辨率和帧率等。支持将本地桌面推流&#xff0c;可指定屏幕区域和帧率等。自动启…

CAN通信-应用

up起来 驱动加载完成&#xff0c;使用ifconfig -a 可以看到两个节点 can0: flags128<NOARP> mtu 16unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX p…

【Vuex+ElementUI】Vuex中取值存值以及异步加载的使用

一、导言 1、引言 Vuex是一个用于Vue.js应用程序的状态管理模式和库。它建立在Vue.js的响应式系统之上&#xff0c;提供了一种集中管理应用程序状态的方式。使用Vuex&#xff0c;您可以将应用程序的状态存储在一个单一的位置&#xff08;即“存储”&#xff09;中&#xff0c;…

MATLAB算法实战应用案例精讲-【图像处理】SLAM技术详解(应用篇)

目录 前言 知识储备 概率论基础 边缘概率 联合概率和独立 独立与条件独立

iPhone15手机拓展坞方案,支持手机快充+传输数据功能

手机拓展坞的组合有何意义&#xff1f;首先是数据存储场景&#xff0c;借助拓展坞扩展出的接口&#xff0c;可以连接U盘、移动硬盘等采用USB接口的设备&#xff0c;实现大文件的快速存储或者流转&#xff1b;其次是图片、视频的读取场景&#xff0c;想要读取相机、无人机SD/TF存…

options.html 页面设计成聊天框,左侧是功能列表,右侧是根据左侧的功能切换成不同的内容。--chatGpt

gpt: 要将 options.html 页面设计成一个聊天框式的界面&#xff0c;其中左侧是功能列表&#xff0c;右侧根据左侧的功能切换成不同的内容&#xff0c;你可以按照以下步骤进行设计和实现&#xff1a; 1. 首先&#xff0c;创建 options.html 文件&#xff0c;并在其中定义基本的…

【Xcode-宏定义配置】

1&#xff0c;项目中配置宏-DEBUG 打开Xcode项目工程 -> Targets -> Build Settings -> Preprocessor Macros -> &#xff0c;在Debug状态下点击右侧&#xff0c;“”添加&#xff0c;在对话框中输入DEBUG1&#xff0c;并保存&#xff0c;注意别把Release给覆盖了…

shell脚本学习

shell是一个用 C 语言编写的程序&#xff0c;是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。实际上Shell是一个命令解释器&#xff0c;它解释由用户输入的命令并且把它们送到内核。不仅如此&#xff0c;Shell有自己的编程语言用于对命令的…

【angular】实现简单的angular国际化(i18n)

文章目录 目标过程运行参考 目标 实现简单的angular国际化。本博客实现中文版和法语版。 将Hello i18n!变为中文版&#xff1a;你好 i18n!或法语版:Bonjour l’i18n !。 过程 创建一个项目&#xff1a; ng new i18nDemo在集成终端中打开。 添加本地化包&#xff1a; ng a…

042:mapboxGL点击某feature点,使其为中心点

第042个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中通过鼠标点击某feature点,让其成为中心点。这里用到了click事件和flyTo的方法。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共113行)相关API参…

spring boot+ vue位置信息大数据综合管理平台源码

spring boot vue位置信息大数据综合管理平台源码 UWB技术的人员定位系统源码 智慧工厂是产业升级的外在表现形式&#xff0c;利用物联网技术加强信息管理的新模式&#xff0c;人员定位管理通过物联网技术、位置信息大数据的综合处理应用&#xff0c;在智慧工厂人员管理方面具有…

大模型之Prompt研究和技巧

大模型之Prompt研究和技巧 大模型之Prompt编写简介组成技术Zero-ShotFew-shotCOTCOT-SCTOTGoTReAct 大模型之Prompt编写 简介 Prompt是是给 AI **模型的指令&#xff0c;**一个简短的文本输入&#xff0c;用于引导AI模型生成特定的回答或执行特定任务。 Prompt是你与语言模型沟…

【方法】PDF不能转换成其它格式如何解决?

想把PDF文件转换成其他格式&#xff0c;比如Word、PPT&#xff0c;却发现无法操作&#xff0c;这是什么情况呢&#xff1f;又该如何解决&#xff1f;下面我们一起来看看吧。 原因1&#xff1a;没有使用PDF编辑器 如果是在线打开PDF&#xff0c;或者使用PDF阅读器打开PDF&…

将 mysql 数据迁移到 clickhouse (最新版)

一、前驱知识 已经在mysql中插入了海量的数据了&#xff0c;这个时候mysql 承载不了这么大的数据&#xff0c;并且数据只需要查询&#xff0c;修改和删除非常少&#xff0c;并且不需要支持事务&#xff0c;这个时候需要换一个底层存储&#xff0c;这里选用的是 clickhouse 来进…

Spring实例化源码解析之ConversionService(十)

前言 ConversionService&#xff08;转换服务&#xff09;是Spring框架中的一个核心接口&#xff0c;用于在不同类型之间进行转换和格式化操作。它提供了一种统一的方式来处理对象之间的类型转换&#xff0c;以及将数据从一种表示形式转换为另一种表示形式。 以下是Conversio…

【论文阅读】面向抽取和理解基于Transformer的自动作文评分模型的隐式评价标准(实验结果部分)

方法 结果 在这一部分&#xff0c;我们展示对于每个模型比较的聚合的统计分析当涉及到计算特征和独立的特征组&#xff08;表格1&#xff09;&#xff0c;抽取功能组和对齐重要功能组&#xff08;表格2&#xff09;&#xff0c;并且最后&#xff0c;我们提供从模型比较&#x…

Linux服务器(银河麒麟、CentOS 7+、CentOS 7+ 等)修改IP地址

打开终端或控制台&#xff0c;以root或具有sudo权限的用户身份登录。根据你的Linux发行版和网络管理工具的不同&#xff0c;相应的命令可能略有不同。使用以下命令编辑网络配置文件&#xff0c;例如eth0网卡的配置文件&#xff1a; 注意&#xff1a;ifcfg-eth0 可能会有不同的命…

解读非托管流动性协议Hover: 差异化、层次化的全新借贷体系

“Hover 是 DeFi 借贷赛道的另辟蹊径者&#xff0c;除了在自身机制&#xff08;借贷模型、治理体系&#xff09;上进行创新获得内生动力外&#xff0c;背靠日渐繁荣的 Kava、Cosmos 生态进一步获得外生动力&#xff0c;发展潜力俱佳” 与 DEX 类似&#xff0c;借贷也是 DeFi 世…