HashMap源码阅读
jdk HashMap源码阅读分析
java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
什么是hash
百度百科:
Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。
这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
1 类结构
共2398行代码,由上到下慢慢看吧
思考:为什么HashMap 继承了AbstractMap后还要实现Map接口,AbstractMap明明已经实现了
1.1 静态常量
初始容量(16),必须为2的幂次方。为了快速计算val在桶的下标,(n-1)&hash
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
最大容量(1073741824)
static final int MAXIMUM_CAPACITY = 1 << 30;
负荷系数(0.75f)
static final float DEFAULT_LOAD_FACTOR = 0.75f
Node链表转化为树结构的阈值
static final int TREEIFY_THRESHOLD = 8;
Node取消树结构的阈值
static final int UNTREEIFY_THRESHOLD = 6;
Node树化的最小表容量
static final int MIN_TREEIFY_CAPACITY = 64;
Class Node结构.node+next?链表?
// 部分代码
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
// 为了解决hash冲突,相同hash的val用链表存储,数量达到TREEIFY_THRESHOLD升级为树
Node<K,V> next;
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
}
1.2 静态方法
hashMap的hash(散列)算法,
好的hash(散列)算法,应该足够均匀,减少hash冲突
已知 jdk hashCode是一个32位int值(Object hashCode()方法)
假设已有 hashCode: 1111 0000 1111 0000 1111 0000 1111 0000
length=64: 0000 0000 0000 0000 0000 0000 0100 0000
length-1=63: 0000 0000 0000 0000 0000 0000 0011 1111
计算此hashcode对应node:nodes[(length -1 ) & hash]
结果=48: 0000 0000 0000 0000 0000 0000 0011 0000
问题:(h >>> 16)的意义?
结论:在length相当大的时候,hashcode的高16位才会参与运算,所以这里jdk
让高16位与低16位一起参与hash(散列),使hash更均匀,减少hash冲突
问题:hash ^ (hash>>>16),为什么是^(异或)
结论:^(异或),& | 这三种运算只有 ^(异或)的结果是均匀的
0|1,0|0,1|1,1|0 : true false true true
0&1,0&0,1&1,1&0 : false flase true flase
0^1,0^0,1^1,1^0 : true false false true
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
x实现Comparable接口,则返回x.class
static Class<?> comparableClassFor(Object x) {
if (x instanceof Comparable) {
Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
if ((c = x.getClass()) == String.class) // bypass checks
return c;
if ((ts = c.getGenericInterfaces()) != null) {
for (int i = 0; i < ts.length; ++i) {
if (((t = ts[i]) instanceof ParameterizedType) &&
((p = (ParameterizedType)t).getRawType() ==
Comparable.class) &&
(as = p.getActualTypeArguments()) != null &&
as.length == 1 && as[0] == c) // type arg is c
return c;
}
}
}
return null;
}
比较方法
static int compareComparables(Class<?> kc, Object k, Object x) {
return x == null || x.getClass() != kc ? 0 : ((Comparable)k).compareTo(x);
}
返回大于等于cap的2的幂次方
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
1.3 Fields
Node数组
transient Node<K,V>[] table;
Entry数组
transient Set<Map.Entry<K,V>> entrySet;
hashMap Node个数
transient int size;
修改次数
transient int modCount;
下一次扩容的阈值
int threshold;
负荷系数
final float loadFactor;
1.4 Public operations
构造方法
public HashMap(int initialCapacity, float loadFactor)
public HashMap(int initialCapacity)
public HashMap()
public HashMap(Map<? extends K, ? extends V> m)
.....省略不重要方法
.......省略lanmda
....... 省略迭代方法
.......
getNode(): get(),containsKey()...等方法都是间接调用这个
final HashMap.Node<K,V> getNode(int hash, Object key) {
HashMap.Node<K,V>[] tab;
HashMap.Node<K,V> first, e;
int n;
K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// Nodes[]中第一个值刚好等于key,直接返回第一个node
if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
return first;
// 第一个值不是这个key,那就开始找Node的next
if ((e = first.next) != null) {
// 已经转化为树结构
if (first instanceof HashMap.TreeNode)
// 调用TreeNode 的getTreeNode
return ((HashMap.TreeNode<K,V>)first).getTreeNode(hash, key);
// 还是单链表,ok开始遍历查找
do {
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
putVal()
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
HashMap.Node<K,V>[] tab;
HashMap.Node<K,V> p;
int n, i;
// 未初始化需要计算新的length
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// 此key在Nodes[]中没有出现过
if ((p = tab[i = (n - 1) & hash]) == null)
// 创建
tab[i] = newNode(hash, key, value, null);
else {
// 执行到这里说明这个hashCode出现过,麻烦起来了.
// p=Nodes[(n-1)%hash]
HashMap.Node<K,V> e;
K k;
// 刚好第一个就是要找的
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// p已经转换成树结构的TreeNode了,交给TreeNode去查找
else if (p instanceof HashMap.TreeNode)
e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 执行到这里说明p还是单链表
// 循环查找,并计个数
for (int binCount = 0; ; ++binCount) {
// 找到底没有找到,说明没有这个Node,创建一个
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// 要是这个链表长度达到树转换阈值,则将此链表转换成树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// 找相同key的Node
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) {
// 执行到这说明之前有这个key,做替换val处理,并返回旧值
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
// 执行到这里说明创建了新的Node
// 添加修改次数
++modCount;
// 检查扩容
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
resize() 初始化或者扩容时调用
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
// 第一部分,计算新的容量和扩容阈值
if (oldCap > 0) {
// 执行到这说明hashMap已经初始化了
// 满了,直接 return oldTab
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 旧容量满足大于16,且扩大一倍后不超过最大size,则计算新阈值(旧阈值*2)
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1;
}
else if (oldThr > 0)
// 执行到这说明是调用了HashMap有参构造方法(int initialCapacity,float loadFactor)创建的
// 容量= 大于等于构造参数且最接近initialCapacity的 2的幂次方
newCap = oldThr;
else {
// 执行到这说明是调用了HashMap无参构造方法创建的,使用默认参数
// 容量=16,阈值=0.75*16=12
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// 再次检测计算一次扩容阈值,疑惑:这段代码为什么不移动到 else if (oldThr > 0)里面?
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
// 第二部分,用新容量建立新的Node[],并迁移旧Node到新Node[]
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
// 遍历旧Node[]
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
// 执行到这说明旧Node[]上,此位置有Node存在,开始处理
oldTab[j] = null;
// 2.1 最简单的情况:这个Node没有next(之前没有发生过hash冲突),直接移动到新Nodes[]
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
// 2.2 这个Node已经升级成 TreeNode,交给TreeNode自己迁移到新Nodes[]
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
// 2.3 这个Node还是个单链表
// 假设 hashCode: 1010 1010 1010 1010 1010 1010 1010 1010 =(2863311530)
//初始 length=32: 0000 0000 0000 0000 0000 0000 0010 0000
// length-1=31: 0000 0000 0000 0000 0000 0000 0001 1111
// index=10; 0000 0000 0000 0000 0000 0000 0000 1010 =(2863311530 & 31)=10
//扩容1次:
// length=64: 0000 0000 0000 0000 0000 0000 0100 0000
// length-1=63:0000 0000 0000 0000 0000 0000 0011 1111
// index=42: 0000 0000 0000 0000 0000 0000 0010 1010 =(2863311530 & 63)=42
//扩容2次:
// length=128: 0000 0000 0000 0000 0000 0000 1000 0000
// length-1=127:0000 0000 0000 0000 0000 0000 0111 1111
// index=42: 0000 0000 0000 0000 0000 0000 0010 1010 =(2863311530 & 127)=42
//扩容3次:
// length=256: 0000 0000 0000 0000 0000 0001 0000 0000
// length-1=255:0000 0000 0000 0000 0000 0000 1111 1111
// index=240: 0000 0000 0000 0000 0000 0000 1010 1010 =(2863311530 & 240)=170
// 规律; 未扩容时 长度=32 下标=10
// 第一次扩容时 长度=64 下标=42
// 第二次扩容时 长度=128 下标=42
// 第三次扩容时 长度=256 下标=240
// 结论:扩容后的下标要么不变,要么等于扩容前(长度+下标)
else { // preserve order
// 扩容后旧链表 总是会成为2条新链表
// 下标不变(=j)的那条链表
Node<K,V> loHead = null, loTail = null;
// 下标变成(旧下标+旧数组长度)=(j+oldCap)的那条链表
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
// 链表中第一个Node
next = e.next;
// 下面开始分别构建这2条链表
// 下标不变的情况
if ((e.hash & oldCap) == 0) {
// head只会设置一次
if (loTail == null)
loHead = e;
// 循环设置tail,最后一个tail.next永远=null
else
loTail.next = e;
loTail = e;
}
// 下标改变的情况
else {
// head只会设置一次
if (hiTail == null)
hiHead = e;
// 循环设置tail,最后一个tail.next永远=null
else
hiTail.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;
}
treeifyBin() 树化 Node链表(hashMapSize未达到最小要求(64)则 resize()而不树化)
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index;
Node<K,V> e;
// hashMap长度未达到最小树化要求(64)
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;
// 这个循环只是把Node链表节点替换为TreeNode节点
// 相当于树化前准备
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);
// 替换链表Node,Nodes[index]= TreeNode
if ((tab[index] = hd) != null)
// 真正树化
hd.treeify(tab);
}
}
removeNode() 移除Node
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;
// 确保Nodes[] 有这个hash
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;
// 依然先判断Node链表第一个元素是不是要找的
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
else if ((e = p.next) != null) {
// 到这说明这个hash对应的Node有多个了
// 升级为TreeNode了,交给TreeNode查找
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);
}
}
// 开始移除操作
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
// 升级为树的化交给TreeNode处理
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
// node是链表头,直接处理
else if (node == p)
tab[index] = node.next;
else
// 链表的删除真简单
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
clear() 清除操作
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;
}
}
containsValue() 是否包含val
public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
if ((tab = table) != null && size > 0) {
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}
1.5 TreeNode
TreeNode 继承自 LinkedHashMap.Entry
LinkedHashMap.Entry 又继承自 HashMap.Node
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
}
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
TreeNode 成员变量结构:
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
// --------------自身-------------
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
// --------------继承自 LinkedHashMap.Entry -------------
Entry<K,V> before, after;
// --------------继承自 HashMap.Node<K,V> -------------
final int hash;
final K key;
V value;
Node<K,V> next;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)