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) 等。…

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

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

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

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

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

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

【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 来进…

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

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

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

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

深度学习DAY3:FFNNLM前馈神经网络语言模型

1 神经网络语言模型NNLM的提出 文章&#xff1a;自然语言处理中的语言模型预训练方法&#xff08;ELMo、GPT和BERT&#xff09; https://www.cnblogs.com/robert-dlut/p/9824346.html 语言模型不需要人工标注语料&#xff08;属于自监督模型&#xff09;&#xff0c;所以语言…

React js原生 详解 HTML 拖放 API(鼠标拖放功能)

最近碰到了个需求&#xff0c;大概就是要通过可视化拖拽的方式配置一个冰柜&#xff0c;需要把预设好的冰柜内部架子模板一个个拖到冰箱内。一开始的想法是用鼠标事件&#xff08;mousedown、mouseup等&#xff09;那一套去实现&#xff0c;能实现但是过程过于复杂&#xff0c;…

qt判断当前日期的当月的最后一天是几号

1、拖个dateTimeEdit在界面上&#xff0c;同时来判断输入的时间的最后一天的日期是什么&#xff1f; int year,month;int monthArr[12]{31,28,31,30,31,30,31,31,30,31,30,31};QDateTime time ui->dateTimeEdit->dateTime();year time.toString("yyyy").toIn…

uniapp 显示icon异常

按照文档创建的uni-ui项目&#xff0c;仿照示例程序写的代码中icon显示异常 &#xe470; 异常情况&#xff1a; 正常情况&#xff1a; 通过比对代码发现&#xff0c;示例程序的App.vue中 有一个引用是问题的关键 正是因为多了这一个引用文件&#xff0c;图表的显示才能正常 …

3d tiles规范boundingVolume属性学习

3d tiles的瓦片&#xff08;Tiles&#xff09;包含一些属性&#xff0c;其中第一项是boundingVolume&#xff1b;下面学习boundingVolume&#xff1b; boundingVolume&#xff0c;这个翻译为边界范围框&#xff0c;如果直译为边界体积可能有问题&#xff0c;其实就是包围盒的意…

[Unity][VR]Passthrough2-创建一个基本的Passthrough应用

上一期我们对PassthroughXR项目做好了基本的项目设置,今天我们就开始构建一个基本的Passthrough应用。 我们还是从基本场景开始。先把默认的main camera删除。因为后续我们会引入OVR Rig对象,这个对象自带Camera用来实现VR视角。 在Project面板我们搜索OVR camera rig。看见…

[Mono Depth/3DOD]单目3D检测基础

1.数据增强 图像放缩和裁剪后&#xff0c;相机内参要做相应变化 import random def random_scale(image, calib, scale_range(0.8, 1.2)):scale random.uniform(*scale_range)width, height image.sizeimage image.resize((int(width * scale), int(height * scale)))cali…

10_10C++

X-mid #include <iostream> using namespace std; class Kun {//算术运算符friend const Kun operator(const Kun &k1,const Kun &k2);friend const Kun operator-(const Kun &k1,const Kun &k2);friend const Kun operator*(const Kun &k1,const Ku…