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;
}

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

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

相关文章

很多应用都是nginx+apache+tomcat

nginx 负责负载均衡&#xff0c;将大量的访问量平衡分配给多个服务器 apache 是用来处理静态html、图片等资源&#xff0c;在对HTML解析、响应等方面比tomcat效率更高。 tomcat 处理JSP等内容&#xff0c;进行后台业务操作。 upstream bbb.com.cn{ server 192.168.10.1:80 ;…

Spring Cloud--从零开始搭建微服务基础环境【三】

&#x1f600;前言 本篇博文是关于Spring Cloud–从零开始搭建微服务基础环境【三】&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;…

使用Fiddler模拟网络

Fiddler已经预置提供了模拟Modem速度的选项&#xff0c;其位置位于&#xff1a; Rules->Performances->Simulate Modem Speeds 勾选该选项后&#xff0c;所有通过Fiddler代理的流量都会变得用56k modem上网一般。 要直观观察限速后的效果&#xff0c;最好使用运行在浏览…

Linux用一键安装包部署禅道(18.5版本)

一、安装 禅道软件下载地址&#xff1a;禅道官方下载地址 - 禅道开源项目管理软件 - 禅道开源项目管理软件 请根据自己的需要下载对应的版本。 官方教程地址: (推荐)Linux用一键安装包 - 禅道使用手册 - 禅道开源项目管理软件 注&#xff1a;Linux 一键安装包必须直接解压到 …

2023-9-3 分解质因数

题目链接&#xff1a;分解质因数 #include <iostream>using namespace std;void divide(int n) {for(int i 2; i < n / i; i ){if(n % i 0){int res 0;while(n % i 0){n / i;res ;}cout << i << << res << endl;}}if(n > 1) cout &l…

虚拟现实(VR)和增强现实(AR)

虚拟现实&#xff08;Virtual Reality&#xff0c;VR&#xff09;和增强现实&#xff08;Augmented Reality&#xff0c;AR&#xff09;是两种前沿的计算机技术&#xff0c;它们正在改变人们与数字世界的互动方式。虚拟现实创造了一个计算机生成的全新虚拟环境&#xff0c;而增…

队列和栈两种数据结构的区别和Python实现

队列和栈是两种数据结构,其内部都是按照固定顺序来存放变量的,二者的区别在于对数据的存取顺序 栈是最后存入的数据最先取出,即后进先出 队列是先存入的数据最先取出,即先进先出 Python实现栈 使用append()方法存入数据,使用pop()方法读取数据 # 定义一个空列表(当做栈使…

linux 内存一致性

linux 出现内存一致性的场景 1、编译器优化 &#xff0c;代码上下没有关联的时候&#xff0c;因为编译优化&#xff0c;会有执行执行顺序不一致的问题&#xff08;多核单核都会出现&#xff09; 2、多核cpu乱序执行&#xff0c;cpu的乱序执行导致内存不一致&#xff08;多核出…

匠心新品:大彩科技超薄7寸WIFI线控器发布,热泵、温控器、智能家电首选!

一、产品介绍 此次发布一款7寸高清全新外壳产品&#xff0c;让HMI人机界面家族再添一新成员。该产品相比其他外壳有以下5个大改动&#xff1a; 1 表面玻璃盖板使用2.5D立体结构&#xff1b; 2 液晶盖板采用一体黑设计&#xff0c;且液晶屏与触摸板是全贴合结构&#xff1b; …

python中浮点数的比较

在Python中&#xff0c;你可以使用比较运算符来比较两个浮点数。常用的比较运算符包括&#xff1a; 等于&#xff1a;不等于&#xff1a;!大于&#xff1a;>小于&#xff1a;<大于等于&#xff1a;>小于等于&#xff1a;< 例如&#xff0c;要比较两个浮点数a和b是…

卡片介绍、EMV卡组织、金融认证---安全行业基础篇2

一、卡片介绍 卡片是一种用于存储和传输数据的可携带式物品&#xff0c;通常由塑料或纸质材料制成。卡片通常具有特定的尺寸和形状&#xff0c;以适应各类读写设备。不同类型的卡片可以用于不同的应用&#xff0c;如身份验证、支付、门禁控制等。 接触卡 接触卡是一种需要与读…

SpringBoot 整合 RabbitMQ

1. 创建 SpringBoot 工程 把版本改为 2.7.14 引入这两个依赖: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springfr…

【高效编程技巧】编程菜鸟和编程大佬的差距究竟在哪里?

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《高效编程技巧》《C语言进阶》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言1.如何写出好的代码&#xff1f;1.2 如何分析一个函数写的怎么样 2. 代码板式的重要性2.1 代码…

el-upload上传图片到七牛云或阿里云

&#xff08;1&#xff09;绑定上传地址&#xff0c;上传数据对象 <el-upload class"upload-demo" :action"uploadUrl" :data"uploadData":on-success"handleSuccess" :file-list"[]" :show-file-list"false"…

linux安装mysql无论如何修改权限和所属用户都出现Permission denied

前言 由于后期需要存储大量数据&#xff0c;因此需要将mysql的数据存储位置设置在大容量的硬盘 最早的时候尝试利用apt-get来安装mysql&#xff0c;按照教程设置新的存储为&#xff0c;包括创建新的data文件夹&#xff0c;和利用sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf和…

vmstat

vmstat VirtualMeomoryStatistics&#xff0c;虚拟内存统计&#xff0c;是Linux中监控内存的常用工具&#xff0c;可对操作系统的虚拟内存、进程、CPU等的整体情况进行监视。 [rootwenzi wenzi]# vmstat procs -----------memory---------- ---swap-- -----io---- -system--…

C#---第21: partial修饰类的特性及应用

0.知识背景 局部类型适用于以下情况&#xff1a; 类型特别大&#xff0c;不宜放在一个文件中实现。一个类型中的一部分代码为自动化工具生成的代码&#xff0c;不宜与我们自己编写的代码混合在一起。需要多人合作编写一个类 局部类型的限制: 局部类型只适用于类、接口、结构&am…

Python Qt学习(九)MainWindow

源代码&#xff1a; # -*- coding: utf-8 -*-# Form implementation generated from reading ui file qt_mainwindow.ui # # Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do n…

【UE 材质】常用向量运算节点——点积、叉积、归一化

目录 一、点积 二、叉积 三、归一化 一、点积 点积&#xff0c;也称为内积或数量积&#xff0c;是一种用于计算两个向量之间关系的操作。对于两个三维向量 A&#xff08;a1,a2,a3&#xff09;和 B(b1,b2,b3)&#xff0c;它们的点积可以用以下公式表示&#xff1a; ABa1​⋅…

大数据课程K13——Spark的距离度量相似度度量

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握Spark的距离度量和相似度度量; ⚪ 掌握Spark的欧氏距离; ⚪ 掌握Spark的曼哈顿距离; ⚪ 掌握Spark的切比雪夫距离; ⚪ 掌握Spark的最小二乘法; 一、距离度量和相似度度量 1. …