static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认的 table 数组容量 aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认加载因子为 0.75
static final int MAXIMUM_CAPACITY = 1 << 30; // 集合最大容量的上限是:2的30次幂
static final int TREEIFY_THRESHOLD = 8; // 链表树化临界值
static final int UNTREEIFY_THRESHOLD = 6; // 树转成链表的临界值
static final int MIN_TREEIFY_CAPACITY = 64; // 树化时数组的最小长度
transient Node<K,V>[] table; // 存放元素的数组
transient Set<Map.Entry<K,V>> entrySet; // 存放元素的缓存
transient int size; // HashMap 中实际元素个数
transient int modCount; // HashMap 修改次数,每个扩容和更改map结构的计数器
int threshold; // table 扩容临界值 数组长度 * 加载因子
final float loadFactor; // table 加载因子
transient Node<K,V>[] table;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table; // 获取旧的哈希表数组
int oldCap = (oldTab == null) ? 0 : oldTab.length; // 获取旧的容量,如果旧哈希表为空,则容量为0
int oldThr = threshold; // 获取旧的阈值
int newCap, newThr = 0; // 定义新的容量和阈值,初始值设为0
if (oldCap > 0) { // 如果旧的容量大于0
if (oldCap >= MAXIMUM_CAPACITY) { // 如果旧容量超过了最大容量
threshold = Integer.MAX_VALUE; // 将阈值设为Integer.MAX_VALUE
return oldTab; // 返回旧的哈希表数组
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && // 计算新的容量,不能超过最大容量
oldCap >= DEFAULT_INITIAL_CAPACITY) // 如果旧容量大于等于默认容量16
newThr = oldThr << 1; // 计算新的阈值,即旧阈值的2倍
}
else if (oldThr > 0) // 如果旧的哈希表数组为空,但旧的阈值不为0
newCap = oldThr; // 直接使用旧的阈值作为新的容量
else { // 如果旧的哈希表数组和阈值都为空
newCap = DEFAULT_INITIAL_CAPACITY; // 使用默认容量16作为新的容量
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 计算新的阈值,即默认容量16乘以加载因子0.75
}
if (newThr == 0) { // 如果新的阈值仍然为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; // 定义节点e
if ((e = oldTab[j]) != null) { // 如果节点e不为空
oldTab[j] = null; // 将旧的哈希表数组对应位置置为空
if (e.next == null) // 如果节点e没有冲突
newTab[e.hash & (newCap - 1)] = e; // 直接将节点e放入新的哈希表数组中
else if (e instanceof TreeNode) // 如果节点e是树节点
((TreeNode<K,V>)e).split(this, newTab, j, oldCap); // 将树节点分裂成两个链表
else { // 如果节点e为普通链表节点
Node<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) { // 如果节点e的hash值与旧容量的按位与结果为0
if (loTail == null) // 如果低位链表尾节点为空
loHead = e; // 将节点e作为低位链表的头节点
else
loTail.next = e; // 将节点e连接到低位链表的尾部
loTail = e; // 更新低位链表的尾节点
}
else { // 如果节点e的hash值与旧容量的按位与结果为1
if (hiTail == null) // 如果高位链表尾节点为空
hiHead = e; // 将节点e作为高位链表的头节点
else
hiTail.next = e; // 将节点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; // 返回新的哈希表数组
}
private static final int MAXIMUM_CAPACITY = 1 << 30;
// 定义最大容量:根据需求定义哈希表的最大容量,例如1 << 30(2^30)。
//
// 检查容量:在添加或调整哈希表大小之前,始终检查当前容量是否已达到最大容量。如果达到最大容量,则停止添加新元素或进行扩容操作。
//
// 调整容量:如果当前容量未达到最大容量且需要扩容时,可以执行相应的扩容操作。这可能涉及创建一个更大的数组,并将原始数据重新散列到新的数组中。
//
// 控制阈值:可以定义负载因子和树化阈值来控制哈希表的大小和性能。负载因子表示哈希表的使用程度,当达到一定程度时,可以触发扩容操作。树化阈值表示将链表转换为树结构的节点数量阈值,以提高查找效率。
private static final int DEFAULT_CAPACITY = 16;
//哈希表的默认初始容量,必须是2的幂(即至少为1)且不超过最大容量。
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//最大可能的(非2的幂)数组大小。toArray和相关方法需要使用该值
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
//哈希表的默认并发级别。此值未使用,但定义是为了与此类的先前版本兼容。
private static final float LOAD_FACTOR = 0.75f;
//哈希表的负载因子。构造函数中对此值的覆盖仅影响初始表容量。实际的浮点值通常不被使用——更简单地使用表达式,如{@code n - (n >>> 2)}用于相关的重新调整大小阈值。
static final int TREEIFY_THRESHOLD = 8;
//链表转换为树的节点数量阈值。当向具有至少这么多节点的桶中添加元素时,桶会被转换为树。该值必须大于2,并且应至少为8,以便与关于在收缩时转换回普通桶的假设相吻合。
//当链表的结点大于8的时候,链表会转换为红黑树
static final int UNTREEIFY_THRESHOLD = 6;
//在调整大小操作期间取消将(拆分的)桶转换为树的节点数量阈值。应小于TREEIFY_THRESHOLD,并且最多为6,以适应删除下的收缩检测
//当树的结点小于6的时候会转换为链表
static final int MIN_TREEIFY_CAPACITY = 64;
//可以将桶转换为树的最小表容量。否则,如果一个桶中的节点太多,将调整表的大小。该值应至少为4 * TREEIFY_THRESHOLD,以避免调整大小和树化阈值之间的冲突。
//当数组的元素大于64,转化为红黑树
private static final int MIN_TRANSFER_STRIDE = 16;
//每个传输步骤的最小重分配次数。范围被细分以允许多个调整大小线程。此值作为下限,以避免调整大小器遇到过多的内存争用。该值应至少是DEFAULT_CAPACITY。
//并行处理的最小值
private static int RESIZE_STAMP_BITS = 16;
//在sizeCtl中用于记录大小戳的位数。对于32位数组,必须至少为6。
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
//可以帮助调整大小的最大线程数。必须适应32 - RESIZE_STAMP_BITS位。
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
//在sizeCtl中记录大小戳的位移量。
static final int MOVED = -1; // 转发节点的哈希值
static final int TREEBIN = -2; // 树的根节点的哈希值
static final int RESERVED = -3; //临时预留的哈希值
static final int HASH_BITS = 0x7fffffff; // 普通节点哈希值的可用位。
static final int NCPU = Runtime.getRuntime().availableProcessors();//CPU的数量,用于限制某些大小。
put
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 检查key和value是否为null,如果是null则抛出NullPointerException异常
if (key == null || value == null) throw new NullPointerException();
// 计算哈希值,使用spread()方法对key.hashCode()进行处理,以减少哈希冲突
int hash = spread(key.hashCode());
// 用于记录链表长度的计数器
int binCount = 0;
// 使用无限循环对ConcurrentHashMap的table进行操作
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 如果table为null或长度为0,则调用initTable()方法进行初始化
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// 如果当前位置tab[i]为空,则使用CAS操作将新节点加入到该位置,这里不需要加锁
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
// 如果当前位置tab[i]的哈希值为MOVED,表示正在进行扩容操作,调用helpTransfer()方法来帮助进行扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
// 在synchronized块中,检查当前位置tab[i]是否仍然等于f,确保其它线程未修改
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
// 如果当前位置tab[i]是普通的链表节点
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
// 查找与key相等的节点
if (e.hash == hash && ((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
// 如果onlyIfAbsent为false,则更新节点的值为value
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
// 遍历到链表的末尾仍未找到与key相等的节点,则在链表末尾添加新节点
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key, value, null);
break;
}
}
}
// 如果当前位置tab[i]是一个红黑树节点(TreeBin)
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
// 调用putTreeVal()方法将节点添加到红黑树中
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
oldVal = p.val;
// 如果onlyIfAbsent为false,则更新节点的值为value
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
// 如果binCount不为0,则判断是否需要将链表转化为红黑树,如果超过了阈值,则调用treeifyBin()方法进行转化
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
// 通过调用addCount()方法来更新ConcurrentHashMap的计数器
addCount(1L, binCount);
// 返回null,表示没有旧值
return null;
}
get
public V get(Object key) {
Node<K,V>[] tab; // 声明哈希表数组
Node<K,V> e, p; // 当前节点和找到的节点
int n, eh; // 数组长度和当前节点的哈希值
K ek; // 当前节点的键
int h = spread(key.hashCode()); // 计算目标键的哈希值,并进行处理以减少哈希冲突
// 检查哈希表数组是否不为空,并且数组长度大于0,同时获取哈希值对应位置的第一个节点e
if ((tab = table) != null && (n = tab.length) > 0 && (e = tabAt(tab, (n - 1) & h)) != null) {
// 判断第一个节点的哈希值是否与目标哈希值相等
if ((eh = e.hash) == h) {
// 如果当前节点的键与目标键相等,则返回当前节点的值
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
} else if (eh < 0) {
// 如果当前节点的哈希值小于0,表示当前节点是一个树节点(TreeBin),调用find()方法在红黑树中查找指定键的节点
return (p = e.find(h, key)) != null ? p.val : null;
}
// 遍历链表中的节点,直到链表末尾
while ((e = e.next) != null) {
// 如果当前节点的哈希值与目标哈希值相等,并且键与目标键相等,则返回当前节点的值
if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
// 如果未找到匹配的节点,则返回null,表示没有找到对应的值
return null;
}
remove
public final void remove() {
Node<K,V> p = current; // 当前节点
if (p == null)
throw new IllegalStateException(); // 如果当前节点为null,则抛出异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException(); // 如果修改计数与预期的修改计数不相等,则抛出异常,表示集合已被修改
current = null; // 将当前节点置为null
K key = p.key; // 获取当前节点的键
removeNode(hash(key), key, null, false, false); // 调用removeNode()方法移除具有指定哈希值和键的节点
expectedModCount = modCount; // 更新预期的修改计数为当前的修改计数
}
resize
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table; // 保存旧的table数组
int oldCap = (oldTab == null) ? 0 : oldTab.length; // 获取旧的table数组的长度,如果为null则长度为0
int oldThr = threshold; // 保存旧的阈值
int newCap, newThr = 0; // 新的容量和阈值
if (oldCap > 0) { // 如果旧的容量大于0
if (oldCap >= MAXIMUM_CAPACITY) { // 如果旧的容量大于等于最大容量
threshold = Integer.MAX_VALUE; // 设置阈值为最大整数值
return oldTab; // 返回旧的table数组
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) {
newThr = oldThr << 1; // 新的阈值为旧的阈值左移一位(即乘以2)
}
}
else if (oldThr > 0) { // 如果旧的阈值大于0
newCap = oldThr; // 新的容量为旧的阈值
}
else { // 否则,使用默认值
newCap = DEFAULT_INITIAL_CAPACITY; // 新的容量为默认初始容量
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 计算新的阈值
}
if (newThr == 0) { // 如果新的阈值为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数组
table = newTab; // 更新table引用为新的table数组
if (oldTab != null) { // 如果旧的table数组不为null
for (int j = 0; j < oldCap; ++j) { // 遍历旧的table数组
Node<K,V> e;
if ((e = oldTab[j]) != null) { // 如果当前位置有节点
oldTab[j] = null; // 将旧的table数组当前位置置为null
if (e.next == null) // 如果当前节点的下一个节点为null
newTab[e.hash & (newCap - 1)] = e; // 直接放入新的table数组对应位置
else if (e instanceof TreeNode) // 如果是树节点
((TreeNode<K,V>)e).split(this, newTab, j, oldCap); // 则进行树节点的拆分操作
else { // 否则(是链表节点),需要保持原来的顺序
Node<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) { // 根据hash值决定放入哪个链表
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
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; // 返回新的table数组
}