当前位置: 首页 > 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);// 构造…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...