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

C语言:动态内存(一篇拿捏动态内存!)
目录 学习目标: 为什么存在动态内存分配 动态内存函数: 1. malloc 和 free 2. calloc 3. realloc 常见的动态内存错误: 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控件的各种操作,例如:QRadioButton分组、默认选中、禁用启用、重置样式等操作。 一、QRadioButton部件提供了一个带有文本标签的单选框(单选按钮)。QRadioButton是一个可以切换选中(chec…...

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

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

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

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

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

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

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

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

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

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

opencv入门-Opencv原理以及Opencv-Python安装
图像的表示 1,位数 计算机采用0/1编码的系统,数字图像也是0/1来记录信息,图像都是8位数图像,包含0~255灰度, 其中0代表最黑,1代表最白 3, 4,OpenCV部署方法 安装OpenCV之前…...

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

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

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

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);// 构造…...

【ICer的脚本练习】脚本使用的思维培养 —— 用例回归
系列的目录说明请见:ICer的脚本练习专栏介绍与全流程目录_尼德兰的喵的博客-CSDN博客 前言 脚本思维本质上是对重复人力操作的汇总与提炼,也就是说如果一件事情可以通过人不需要大量思考的按部就班操作能够完成,那么理论上脚本就可以进行替代。那么什么时候需要写脚本呢?这…...

【axios网络请求库】认识Axios库;axios发送请求、创建实例、创建拦截器、封装请求
目录 1_认识Axios库2_axios发送请求3_axios创建实例4_axios的拦截器5_axios请求封装 1_认识Axios库 功能特点: 在浏览器中发送 XMLHttpRequests 请求在 node.js 中发送 http请求支持 Promise API拦截请求和响应转换请求和响应数据 2_axios发送请求 支持多种请求方式: axios…...

Android——基本控件(下)(二十一)
1. 数据存储 1.1 知识点 (1)掌握Android数据存储的分类; (2)可以使用SharedPreferences存储数据。 1.2 具体内容 对于我们数据的存储而言,Android一共提供了5个数据存储的方式:SharedPrefe…...

websocket基础
下面就以代码来进行说明 1,先导入websocket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 2.编写websocket相关bean管理配置 Config…...

游戏思考30(补充版):关于逆水寒铁牢关副本、白石副本和技能的一些注释(2023/0902)
前期介绍 我是一名逆水寒的玩家,做一些游戏的笔记当作攻略记录下来,荣光不朽-帝霸来源视频连接 传送门 一、旧版铁牢关(非逆水寒老兵服) (1)老一:巨鹰 1)机制一:三阵风…...

【数据蒸馏】静态数据蒸馏方法汇总
基于几何的方法 基于几何的方法假设在特征空间中彼此接近的数据点往往具有相似的属性。因此,基于几何的方法试图移除那些提供冗余信息的数据点,剩下的数据点形成一个核心集合S,其中jSj ≤ jTj。 Herding(聚集) 是一…...

Cortex-A7 架构
参考《 Cortex-A7 Technical ReferenceManua.pdf 》和《 ARM Cortex-A(armV7) 编程手 册 V4.0.pdf 》 【 正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6学习 1.Cortex-A7 MPCore 简介 I.MX6UL 使用的是 Cortex-A7 架构,Cortex-A7 MPcore 处理器支持 1~4 核&#…...

2023年“羊城杯”网络安全大赛 Web方向题解wp 全
团队名称:ZhangSan 序号:11 不得不说今年本科组打的是真激烈,初出茅庐的小后生没见过这场面QAQ~ D0n’t pl4y g4m3!!! 简单记录一下,实际做题踩坑很多,尝试很多。 先扫了个目录,扫出start.sh 内容如下…...

Matlab——二维绘图(最为详细,附上相关实例)
为了帮助各位同学备战数学建模和学习Matlab的使用,今天我们来聊一聊 Matlab 中的绘图技巧吧!对于 Matlab 这样的科学计算软件来说,绘图是非常重要的一项功能。在数据处理和分析时,良好的绘图技巧能够更直观地呈现数据,…...

JVM学习(四)--内存问题分析思路
linux获取jvm当前dump文件 命令行为:jmap -dump:file[文件名] [pid] 然后等待生成dump文件,生成的dump文件就在当前目录下。如下图: 然后就可以下载到本地,用本地jdk里自带的jvisualvm来解析文件。 在用本地的jvisualvm解析之前…...