当前位置: 首页 > news >正文

HashMap源码阅读(一)

HashMap继承抽象类AbstractMap,AbstractMap抽象类实现了Map接口

一、HashMap中的静态常量

//默认初始容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大长度
static final int MAXIMUM_CAPACITY = 1 << 30;
//负载因子,map中存储的数据在达到负载因子时需要进行扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//哈希桶中存储的链表长度的阈值,当链表长度达到阈值时会转化为红黑树-->树化
static final int TREEIFY_THRESHOLD = 8;
//当哈希桶中存储的链表的长度小于该阈值时,如果发生了树化,则会将树砖换成链表-->反树化
static final int UNTREEIFY_THRESHOLD = 6;
//用于指示哈希表进行重新哈希操作时,何时会将链表转换为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;

二、HashMap中的Node节点

Node节点是用于存储存储哈希表中的键值对的结构通过nent变量,将出现冲突的元素连成一个链表

static class Node<K,V> implements Map.Entry<K,V> {final int hash;//元素的hash值final K key;//元素的KeyV value;//元素的值Node<K,V> next;//该元素所连接的下一个节点//构造方法Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}public final K getKey()        { return key; }public final V getValue()      { return value; }public final String toString() { return key + "=" + value; }public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}//替换Node里面的valuepublic final V setValue(V newValue) {V oldValue = value;value = newValue;return oldValue;}public final boolean equals(Object o) {if (o == this)return true;return o instanceof Map.Entry<?, ?> e&& Objects.equals(key, e.getKey())&& Objects.equals(value, e.getValue());}
}

三、HashMap中的成员变量

//用于存储所有链表的头节点
transient Node<K,V>[] table;
//保存缓存
transient Set<Map.Entry<K,V>> entrySet;
//map的实际长度
transient int size;
transient int modCount;
//用于调整容量的笑一个容量值
int threshold;
//实际的负载因子
final float loadFactor;

四、HashMap的构造函数

public HashMap(int initialCapacity, float loadFactor) {//判断初始长度是否合法if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);//判断设置的初始长度是否大于hash表所设置的最大的长if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;//判断负载因子是否合法if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;//初始长度需要是2^n形式this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//构造方法中传入一个map创建对象
public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;//将参数中的map添加到当前的map中putMapEntries(m, false);
}

五、Map中的简单方法

1、将一个map里面的多有值添加到当前map中

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {int s = m.size();if (s > 0) {if (table == null) { // 初始化//获取数组的最小长度float ft = ((float)s / loadFactor) + 1.0F;//判断获取的最小长度是否大于map所支持的最大长度int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);if (t > threshold)threshold = tableSizeFor(t);} else {//判断传入的map的长度是否大于实际的,并进行扩容while (s > threshold && table.length < MAXIMUM_CAPACITY)resize();}//遍历参数中的map并将map里面的键值对存储在当前的map中for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {K key = e.getKey();V value = e.getValue();putVal(hash(key), key, value, false, evict);}}
}

2、查询map的长度和判空

//查询map长度
public int size() {return size;
}
//判断map是否为空
public boolean isEmpty() {return size == 0;
}

3、根据键进行查询

3.1根据键查找值
//根据key查询value
public V get(Object key) {Node<K,V> e;return (e = getNode(key)) == null ? null : e.value;
}
3.2根据键查找Node
final Node<K,V> getNode(Object key) {Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;//判断table[(n - 1) & (hash = hash(key))]中是否为null,并对数据进行赋值if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & (hash = hash(key))]) != null) {//判断第一个结点if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))return first;if ((e = first.next) != null) {//判断该节点是否树化if (first instanceof TreeNode)return ((TreeNode<K,V>)first).getTreeNode(hash, key);//没有树化,通过遍历查找keydo {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null;
}

3.3判断键是否存在

public boolean containsKey(Object key) {return getNode(key) != null;
}

4、添加

4.1添加
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}
/**
*	hash:key的hash值
*	key
*	value
*	onlyIfAbsent:为true是,出现一样的key不会覆盖value
*	evict:为true时,处于创建模式
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//p为当前节点-->currentNodeNode<K,V>[] tab; Node<K,V> p; int n, i;//判断table是否为空,若为空执行resize方法进行初始化if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//判断(n - 1) & hash下标处是否为空,若为空则添加节点为头节点,直接创建if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))//判断key与头节点的key是否相同e = p;else if (p instanceof TreeNode)//判断链表是否树化,直接向树中添加节点e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {//判断是否是最后一个节点if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);//判断是否达到树化的阈值,对链表树化,或对数组进行扩容if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}//判断当前节点的key是否与key相同if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;//e = p.next,改行代码等价于p = p.nextp = e;}}//e != null => map中存在key:将旧值改为新值if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;//判断是否达到扩容的阈值if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}
4.2将一个map添加到当前map中
public void putAll(Map<? extends K, ? extends V> m) {putMapEntries(m, true);
}

5、扩容和初始化方法

final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;//旧的table的长度int oldCap = (oldTab == null) ? 0 : oldTab.length;//threshole:下一个容量int oldThr = threshold;//newCal:新table的长度,newThr:新的thresholdint newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold}else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;//将就的值全部填入新的数组中if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;//垃圾回收if (e.next == null)//只有一个节点newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)//已树化,将树进行拆分((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // preserve orderNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;
}

6、链表长度过长,树化或扩容

final void treeifyBin(Node<K,V>[] tab, int hash) {int n, index; Node<K,V> e;//if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)resize();else if ((e = tab[index = (n - 1) & hash]) != null) {TreeNode<K,V> hd = null, tl = null;do {TreeNode<K,V> p = replacementTreeNode(e, null);if (tl == null)hd = p;else {p.prev = tl;tl.next = p;}tl = p;} while ((e = e.next) != null);if ((tab[index] = hd) != null)hd.treeify(tab);}
}

7、删除

public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;
}
//matchValue:当value相同时删除否则不删除
final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node<K,V>[] tab; Node<K,V> p; int n, index;//判断是否为空(数组、key所对应的下标处)if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {Node<K,V> node = null, e; K k; V v;//判断头节点keyif (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))node = p;else if ((e = p.next) != null) {if (p instanceof TreeNode)node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {do {if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}//判断key对应的node是否存在if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;elsep.next = node.next;++modCount;--size;afterNodeRemoval(node);return node;}}return null;
}

8、清空链表

public void clear() {Node<K,V>[] tab;modCount++;if ((tab = table) != null && size > 0) {size = 0;for (int i = 0; i < tab.length; ++i)tab[i] = null;}
}

9、判断链表中是否存在某个值

public boolean containsValue(Object value) {Node<K,V>[] tab; V v;if ((tab = table) != null && size > 0) {for (Node<K,V> e : tab) {for (; e != null; e = e.next) {if ((v = e.value) == value ||(value != null && value.equals(v)))return true;}}}return false;
}

相关文章:

HashMap源码阅读(一)

HashMap继承抽象类AbstractMap&#xff0c;AbstractMap抽象类实现了Map接口 一、HashMap中的静态常量 //默认初始容量 static final int DEFAULT_INITIAL_CAPACITY 1 << 4; // aka 16 //最大长度 static final int MAXIMUM_CAPACITY 1 << 30; //负载因子&#…...

C语言:动态内存(一篇拿捏动态内存!)

目录 学习目标&#xff1a; 为什么存在动态内存分配 动态内存函数&#xff1a; 1. malloc 和 free 2. calloc 3. realloc 常见的动态内存错误&#xff1a; 1. 对NULL指针的解引用操作 2. 对动态开辟空间的越界访问 3. 对非动态开辟内存使用free释放 4. 使用free释…...

Lua - 替换字符串中的特殊字符

//替换指定串 s string.gsub("Lua is good", "good", "bad") print(s) --> Lua is bad//替换特殊字符 a "我们使用$"; b string.gsub(a, "%$", "RMB"); print(b) --> 我们使用RMB//替换反斜杠 path …...

按钮控件之3---QRadioButton 单选按钮/单选框控件

本文详细的介绍了QRadioButton控件的各种操作&#xff0c;例如&#xff1a;QRadioButton分组、默认选中、禁用启用、重置样式等操作。 一、QRadioButton部件提供了一个带有文本标签的单选框&#xff08;单选按钮&#xff09;。QRadioButton是一个可以切换选中&#xff08;chec…...

基于STM32设计的游戏姿态数据手套

基于STM32设计的游戏姿态数据手套 一、项目背景 随着虚拟现实技术的发展,人机交互越来越朝着多通道、自然化的方向发展,由原来的以机器为中心向以人为中心发展。按照行业通用用途设计的高端数据手套,可以用于测量人手指动作,如搓捻、对掌等动作,广泛应用于人手的运动捕捉…...

react跳转页面redux数据被清除

关键代码如下&#xff0c;页面中有根据redux中state展示的数据&#xff0c;然后在组件卸载的时候会清空redux中存的数据&#xff0c;点击a标签可以打开新的标签页&#xff0c;如下代码会在打开新的标签页&#xff0c;组件卸载&#xff0c;清空redux数据&#xff0c;页面展示的也…...

Spring Cloud 微服务2

Eureka 注册中心&#xff0c;服务的自动注册、发现、状态监控 Ribbon 负载均衡&#xff0c;Eureka中已经集成了负载均衡组件 Hystrix 熔断器&#xff0c;用于隔离访问远程服务、第三方库&#xff0c;防止出现级联失败。 Feign 远程调用&#xff0c;将Rest的请求进行隐藏&a…...

侯捷课程笔记(一)(传统c++语法,类内容)

侯捷课程笔记&#xff08;一&#xff09;&#xff08;传统c语法&#xff0c;类内容&#xff09; 2023-09-03更新&#xff1a; 本小节已经完结&#xff0c;只会进行小修改 埋下了一些坑&#xff0c;后面会单独讲或者起新章节讲 最近在学习侯捷的一些课程&#xff0c;虽然其中大部…...

自动化安装Nginx脚本:简化您的服务器配置

在如今的网络世界中&#xff0c;Nginx作为一款高性能的Web服务器和反向代理服务器&#xff0c;扮演着至关重要的角色。然而&#xff0c;手动安装和配置Nginx可能会耗费大量时间和精力&#xff0c;特别是对于那些对Linux系统不太熟悉的人来说。幸运的是&#xff0c;我们为您带来…...

通过es索引生命周期策略删除日志索引

通过es索引生命周期策略删除日志索引 在es 7.x版本之后&#xff0c;多了个索引生命周期的概念&#xff0c;可以一系列的设置&#xff0c;给新生成的索引绑定生命周期策略&#xff0c;到期后&#xff0c;索引自动删除。 也可以通过linux定时任务实现&#xff0c;请查看另一篇文章…...

网络实验 VlAN 中 Trunk Access端口的说明及实验

网络实验 VlAN 中 Trunk Access端口的说明及实验 VlAN 虚拟局域网技术Access端口 工作原理Trunk端口 工作原理简单实验&#xff08;一&#xff09;划分不同Vlan&#xff0c;实现vlan内部通信拓朴图主机IP 配置划分Vlanping 测试 &#xff08;二&#xff09;跨交换机实现VLAN间通…...

打包个七夕exe玩玩

前段时间七夕 当别的哥们都在酒店不要不要的时候 身为程序员的我 还在单位群收到收到 正好后来看到大佬些的这个 https://www.52pojie.cn/thread-1823963-1-1.html 这个贱 我必须要犯&#xff0c;可是我也不能直接给他装个python吧 多麻烦 就这几个弹窗 好low 加上bgm 再打包成…...

ReactNative 井字游戏 实战

效果展示 需要的插件准备 此实战项目需要用到两个插件。 react-native-snackbar 底部信息提示组件。 react-native-vector-icons 图标组件。 安装组件&#xff1a; npm i react-native-snackbar npm i react-native-vector-icons npm i types/react-native-vector-icons /…...

五-垃圾收集器G1ZGC详解

回顾CMS垃圾收集器 G1垃圾收集器 G1是一款面向服务器的垃圾收集器&#xff0c;主要针对配备多颗处理器及大容量处理的机器。以及高概率满足GC停顿时间要求的同时&#xff0c;还具备高吞吐量性能特征 物理上没有明显的物理概念&#xff0c;但是逻辑上还是有分代概念 物理上分…...

opencv入门-Opencv原理以及Opencv-Python安装

图像的表示 1&#xff0c;位数 计算机采用0/1编码的系统&#xff0c;数字图像也是0/1来记录信息&#xff0c;图像都是8位数图像&#xff0c;包含0~255灰度&#xff0c; 其中0代表最黑&#xff0c;1代表最白 3&#xff0c; 4&#xff0c;OpenCV部署方法 安装OpenCV之前…...

k8s etcd 简介

Etcd是CoreOS基于Raft协议开发的分布式key-value存储&#xff0c;可用于服务发现、共享配置以及一致性保障&#xff08;如数据库选主、分布式锁等&#xff09;。 如&#xff0c;Etcd也可以作为微服务的注册中心&#xff0c;比如SpringCloud也基于ETCD实现了注册中心功能&#…...

分页功能实现

大家好 , 我是苏麟 , 今天聊一聊分页功能 . Page分页构造器是mybatisplus包中的一个分页类 . Page分页 引入依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</ver…...

普通制造型企业,如何成就“链主品牌

“链主品牌”通常掌握产业链主导地位&#xff0c;对于普通制造型企业看起来是遥不可及的事情&#xff0c;事实上并非如此。从洞察穿越周期的“链主品牌”规律来看&#xff0c;做螺丝起家的伍尔特、做宠物牵引绳的福莱希等小企业也可以成为“链主品牌”。另外&#xff0c;由于新…...

04_22 vma(进程下的每个虚拟内存区域查看)对象实战

前言 vma不太懂的可以往前翻 03_008内存映射原理_虚拟内存区域vm_area_struct详解,和mmap系统钓调用及物理内存结构体完全分析 vam 虚拟内存区域 每个进程下有多个vma 这次是查看每个vma的起始地址 结束地址和大小使用 1.进程在用户空间调用mmap也就是上面那个函数。 2.在当前…...

QWidget的ui界面绘制成图片

文章目录 源文件源码解释效果修复图片清晰度 源文件 #include "widget.h" #include "ui_widget.h"#include <QPixmap> #include <QDir>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);// 构造…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...