HashMap
HashMap 能解决什么问题?什么时候使用 HashMap?
1)HashMap 是基于哈希表的 Map 接口实现,允许使用 null 键和 null 值。
2)假定哈希函数将元素均匀地分布在各个桶中,HashMap 的 get 和 put 方法提供稳定的性能。
3)影响 HashMap 性能的两个参数:初始容量和加载因子,当元素个数超出初始容量和加载因子乘积时,
将进行扩容,哈希表将具有大约两倍的桶数。默认的初始容量为 16,默认的加载因子为 0.75,当需要添加大量元素时,
提供合适的初始容量和加载因子可以避免频繁的 rehash 操作以提高性能。
4)HashMap 不是线程安全的,HashMap 视图方法返回的迭代器都是快速失败的,面对并发修改,HashMap 将抛出 ConcurrentModificationException 异常。
如何使用 HashMap?
1)需要保存对象间映射关系时,可以使用 HashMap。
2)当明确知道元素个数范围时,可以指定一个初始化容量来减少扩容次数。
3)降低 HashMap 的加载因子可以减少 hash 碰撞,通过减少指定键的比较次数来提升读写性能,但是会造成更多内存的浪费。
使用 HashMap 有什么风险?
1)HashMap 桶的数量为 2 的幂,存在一定的内存浪费。
2)多线程环境下使用 HashMap,在扩容时可能导致死循环。
HashMap 核心操作的实现原理?
/**
* 默认的初始 Bucket 数量,必须是 2 的幂
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* 哈希表最大的 Bucket 数量
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 未在构造函数中指定时使用的默认加载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 单向链表转换为红黑树时的链表长度
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 红黑树转换为单向链表时的元素个数
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 单向链表转换为红黑树时,最小的 Bucket 数量。
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* 创建一个初始容量为 16、加载因子为 0.75 的空 HashMap 实例
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
* 创建一个初始容量为 initialCapacity、加载因子为 0.75 的空 HashMap 实例
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 创建一个初始容量为 initialCapacity、加载因子为 loadFactor 的空 HashMap 实例
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
}
// HashMap 的最大 Bucket 数量为 1 << 30
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
}
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
}
this.loadFactor = loadFactor;
this.threshold = HashMap.tableSizeFor(initialCapacity);
}
/**
* 基于目标容量计算最接近的 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;
}
/**
* 底层存储 Bucket 的节点数组,在第一次使用时完成初始化,并按需自动扩容,
* 数组长度必须是 2 的幂。
*/
transient Node<K,V>[] table;
/**
* 往 HashMap 中添加新的元素,如果目标键已经存在,则旧值被替换为新值,并返回旧值。
*/
@Override
public V put(K key, V value) {
return putVal(HashMap.hash(key), key, value, false, true);
}
/**
* 计算键的哈希值
*/
static final int hash(Object key) {
int h;
// HashMap 允许使用 null 键
return key == null ? 0 : (h = key.hashCode()) ^ h >>> 16;
}
/**
* 基础的哈希表节点
*/
static class Node<K,V> implements Map.Entry<K,V> {
/**
* 键的哈希值
*/
final int hash;
/**
* 键
*/
final K key;
/**
* 值
*/
V value;
/**
* 单向链表的下一个节点
*/
Node<K,V> next;
}
/**
* @param hash 键的哈希值
* @param key 键
* @param value 值
* @param onlyIfAbsent 为 true 时,不会覆盖已经存在的值
* @param evict 为 false 时,哈希表处于创建模式
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 1)第一次添加元素时会进入该分支,
if ((tab = table) == null || (n = tab.length) == 0) {
// 读取 table 的长度
n = (tab = resize()).length;
}
// 通过【table.length-1 & 键的哈希值】定位 bucket 的位置,如果该位置为空,则创建新的节点完成添加。
if ((p = tab[i = n - 1 & hash]) == null) {
// 1)创建一个普通节点
tab[i] = newNode(hash, key, value, null);
} else {
// 2)哈希表的键发生碰撞,目标 bucket 已经有元素存在
Node<K,V> e; K k;
// 2-1)bucket 头元素和新增元素的哈希值相等并且键值也相等
if (p.hash == hash &&
((k = p.key) == key || key != null && key.equals(k))) {
e = p;
// 2-3)当前 bucket 已经是一颗红黑树
} else if (p instanceof TreeNode) {
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
} else {
// 2-2)遍历单向链表完成元素添加
for (int binCount = 0; ; ++binCount) {
// 当前节点的后置节点为空
if ((e = p.next) == null) {
// 创建一个新的节点,加入到当前节点后面
p.next = newNode(hash, key, value, null);
// 如果单向链表的元素个数大于等于 9 个
if (binCount >= TREEIFY_THRESHOLD - 1) {
// 单向链表红黑树化
treeifyBin(tab, hash);
}
break;
}
// 如果当前节点的哈希值和新增元素的哈希值相等,并且键值也相等
if (e.hash == hash &&
((k = e.key) == key || key != null && key.equals(k))) {
// 元素已经找到,则退出循环
break;
}
// 更新迭代节点为单向链表的下一个节点
p = e;
}
}
// 目标节点已经存在
if (e != null) { // existing mapping for key
// 读取旧值
final V oldValue = e.value;
// 如果允许覆盖或原来的值为 null
if (!onlyIfAbsent || oldValue == null) {
// 写入新值
e.value = value;
}
// 被 LinkedHashMap 使用的钩子函数
afterNodeAccess(e);
// 返回旧值
return oldValue;
}
}
// 增加并发修改计数值
++modCount;
// 如果元素个数超出阈值,则进行扩容
if (++size > threshold) {
resize();
}
// 被 LinkedHashMap 使用的钩子函数
afterNodeInsertion(evict);
return null;
}
/**
* 初始化或将 table 扩容为原来的两倍,下标为 index 的 bucket 中的元素只能迁移到新 table 中下标为 index
* 处或 index+oldCapacity 处。
*/
final Node<K,V>[] resize() {
// 暂存旧的 table
final Node<K,V>[] oldTab = table;
// 读取旧的哈希表容量
final int oldCap = oldTab == null ? 0 : oldTab.length;
// 读取旧的扩容阈值
final int oldThr = threshold;
int newCap, newThr = 0;
// 3)执行第二次扩容
if (oldCap > 0) {
// 旧容量已经达到最大值
if (oldCap >= MAXIMUM_CAPACITY) {
// 阈值设置为 Integer.MAX_VALUE
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 旧容量扩大一倍也小于最大容量值,并且旧容量大于等于初始化容量 16
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
{
// 阈值扩大一倍
newThr = oldThr << 1; // double threshold
}
}
// 2)基于带参数构造函数创建 HashMap 实例时,第一次扩容会进入该分支
else if (oldThr > 0) {
newCap = oldThr;
} else { // zero initial threshold signifies using defaults
// 1)基于无参构造函数创建的哈希表第一次扩容时,设置初始容量为 16,扩容阈值为 12.
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
final float ft = newCap * loadFactor;
newThr = newCap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE;
}
// 写入新的阈值
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
// 创建新的 table
final Node<K,V>[] newTab = new Node[newCap];
// 写入新的 table
table = newTab;
// 非第一次扩容
if (oldTab != null) {
// 循环遍历每个 bucket
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
// 读取 bucket 首节点
if ((e = oldTab[j]) != null) {
// 首节点不为 null,需要执行数据迁移
oldTab[j] = null;
// 1)bucket 中只有一个元素
if (e.next == null) {
// 直接将其赋值到新 table 的指定 bucket 中即可
newTab[e.hash & newCap - 1] = e;
// 2)旧的 bucket 已经是一颗红黑树,则需要执行拆分
} else if (e instanceof TreeNode) {
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
} else { // preserve order
/**
* 3)旧的 bucket 是一个单向链表
* loHead 表示 lowHead,低位的头节点
* loTail 表示 lowTail,低位的尾节点
* hiHead 表示 highHead,高位的头节点
* hiTail 表示 highTail,高位的为节点
*/
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
// 读取第二个节点
next = e.next;
// 1)节点哈希值和旧容量相与为 0,则将该节点放在低位 bucket 中
if ((e.hash & oldCap) == 0) {
// 如果是低位链表的第一个节点
if (loTail == null) {
// 设置头节点为 e
loHead = e;
} else {
// 将当前节点加入链表尾部
loTail.next = e;
}
// 更新低位链表尾节点为当前迭代节点
loTail = e;
}
// 2)该节点需要放置在高位 bucket 中
else {
// 如果是高位链表的第一个节点
if (hiTail == null) {
// 设置头节点为 e
hiHead = e;
} else {
// 将当前节点加入链表尾部
hiTail.next = e;
}
// 更新高位链表尾节点为当前迭代节点
hiTail = e;
}
// 迭代下一个节点
} while ((e = next) != null);
// 低位链表尾节点不为 null
if (loTail != null) {
// 尾节点的后置节点置为空
loTail.next = null;
// 将头结点设置为低位 bucket 的首节点
newTab[j] = loHead;
}
// 高位链表尾节点不为 null
if (hiTail != null) {
// 尾节点的后置节点置为空
hiTail.next = null;
// 将头结点设置为高位 bucket 的首节点
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
// 创建一个普通节点
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
/**
* 尝试进行单向链表的红黑树化
*/
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
// 1)如果 table 的长度小于 64
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) {
// 则执行扩容操作
resize();
// 2)获取到单向链表的头节点,并暂存在 e 中
} else if ((e = tab[index = n - 1 & hash]) != null) {
// hd 表示头节点 head,tl 表示尾节点 tail
TreeNode<K,V> hd = null, tl = null;
do {
// 将当前节点替换为树节点
final 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);
// 将头结点作为 bucket 的第一个节点
if ((tab[index] = hd) != null) {
// 执行红黑树化操作
hd.treeify(tab);
}
}
}
// For treeifyBin
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
return new TreeNode<>(p.hash, p.key, p.value, next);
}
/**
* 执行单向链表的红黑树化过程
*/
void treeify(Node<K,V>[] tab) {
TreeNode<K,V> root = null;
for (TreeNode<K,V> x = this, next; x != null; x = next) {
// 暂存当前节点的后置节点
next = (TreeNode<K,V>)x.next;
// 节点的左右子节点置为 null
x.left = x.right = null;
// 1)还不存在根节点,则说明当前节点是第一个节点
if (root == null) {
// 根节点的父节点为 null
x.parent = null;
// 根节点为黑色
x.red = false;
root = x;
}
// 2)根节点已经存在
else {
// 读取节点的键
final K k = x.key;
// 读取节点的哈希值
final int h = x.hash;
// 键比较器
Class<?> kc = null;
for (TreeNode<K,V> p = root;;) {
/**
* dir 表示 directory,-1 表示左子树,+1 表示右子树
* ph 表示 parent hash,父节点的哈希值
* pk 表示 parent key,父节点的键
*/
int dir, ph;
// 读取父节点的键
final K pk = p.key;
// 父节点的哈希值比当前节点大,则当前节点位于左子树
if ((ph = p.hash) > h) {
dir = -1;
// 父节点的哈希值比当前节点小,则当前节点位于右子树
} else if (ph < h) {
dir = 1;
} else if (kc == null &&
(kc = HashMap.comparableClassFor(k)) == null ||
(dir = HashMap.compareComparables(kc, k, pk)) == 0) {
dir = TreeNode.tieBreakOrder(k, pk);
}
// 暂存当前节点的父节点
final TreeNode<K,V> xp = p;
// 父节点的左子节点或右子节点为空
if ((p = dir <= 0 ? p.left : p.right) == null) {
// 更新当前节点的父节点
x.parent = xp;
// 新节点位于父节点的左侧
if (dir <= 0) {
xp.left = x;
// 新节点位于父节点的右侧
} else {
xp.right = x;
}
// 平衡插入
root = TreeNode.balanceInsertion(root, x);
break;
}
}
}
}
// 确保给定的 root 节点是红黑树的第一个节点
TreeNode.moveRootToFront(tab, root);
}
/**
* 如果 X 实现了 Comparable 接口,则返回 X 的运行时类型,否则返回 null
*/
static Class<?> comparableClassFor(Object x) {
if (x instanceof Comparable) {
Class<?> c; Type[] ts, as; ParameterizedType p;
// 字符串类型优先处理
if ((c = x.getClass()) == String.class) {
return c;
}
// 目标对象的运行时类型 c 直接实现的接口列表
if ((ts = c.getGenericInterfaces()) != null) {
for (final Type t : ts) {
// t 是一个泛型接口
if (t instanceof ParameterizedType &&
// 获取泛型接口的接口类型,并且 t 是 Comparable 接口
(p = (ParameterizedType) t).getRawType() ==
Comparable.class &&
// 获取泛型接口的实际参数类型数组
(as = p.getActualTypeArguments()) != null &&
// 实际参数类型数组长度为 1,即只有一个泛型参数,并且其实际类型就是对象的类型
as.length == 1 && as[0] == c) {
// 返回对象类型
return c;
}
}
}
}
return null;
}
/**
* Returns k.compareTo(x) if x matches kc (k's screened comparable
* class), else 0.
*/
@SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
static int compareComparables(Class<?> kc, Object k, Object x) {
// k 和 x 的运行时类型不一致,则返回 0,否则返回比较结果
return x == null || x.getClass() != kc ? 0 :
((Comparable)k).compareTo(x);
}
static int tieBreakOrder(Object a, Object b) {
int d;
if (a == null || b == null ||
// 如果 a 和 b 的运行时类型不一致,则比较他们的类全名
(d = a.getClass().getName().
compareTo(b.getClass().getName())) == 0) {
// a 和 b 的运行时类型一致,一般会返回 -1
d = System.identityHashCode(a) <= System.identityHashCode(b) ?
-1 : 1;
}
return d;
}
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
// 新插入节点的颜色为红色
x.red = true;
/**
* xp 表示 x parent,新插入节点的父节点
* xpp 表示 x parent parent,新插入节点的祖父节点
* xppl 表示 x parent parent left,新插入节点的祖父节点的左节点
* xppr 表示 x parent parent right,新插入节点的祖父节点的右节点
*/
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
// 新插入节点就是根节点本身
if ((xp = x.parent) == null) {
// 根节点是黑色的
x.red = false;
// 返回根节点
return x;
}
/**
* 1)x 节点的父节点为黑色,则红色节点可以直接插入,直接返回 root 无需变换
* 2)x 的父节点为红色,并且父节点就是 root?【因为根节点必须是黑色的,直接插入红色节点无需变换】
*/
else if (!xp.red || (xpp = xp.parent) == null) {
return root;
}
/**
* 进入此分支
* 1)父节点是红色的,当前节点也是红色的,遇到两个相连的红色节点时,需要进行旋转变换。
* 2)x 的祖父节点不为 null。
*/
// x 的父节点是祖父节点的左孩子
if (xp == (xppl = xpp.left)) {
/**
* xpp
* / \
* [红]xp xppr[红]
* / \
* 此种情况下只需要执行颜色变换
*/
if ((xppr = xpp.right) != null && xppr.red) {
// 祖父节点的左右孩子都设置为 黑色
xppr.red = false;
xp.red = false;
// 祖父节点设置为红色
xpp.red = true;
// 暂存祖父节点
x = xpp;
}
else {
/**
* xpp
* /
* [红]xp
* / \
* x[红]
* 此种情况下需要执行左旋
*/
if (x == xp.right) {
// x 暂存为父节点
root = TreeNode.rotateLeft(root, x = xp);
// 暂存祖父节点
xpp = (xp = x.parent) == null ? null : xp.parent;
}
/**
* xpp
* /
* [红]xp
* /
* [红]x
* 原本是这种结构,或是由于执行了左旋变成这种结构,则需要进行一次右旋
*/
if (xp != null) {
// 设置父节点颜色为黑色
xp.red = false;
// 如果祖父节点不为 null
if (xpp != null) {
// 设置祖父节点颜色为红色
xpp.red = true;
// 执行右旋
root = TreeNode.rotateRight(root, xpp);
}
}
}
}
// x 的父节点是祖父节点的右孩子
else {
/**
* xpp
* /\
* [红]xppl xp[红]
* 此种情况下只需要进行颜色变换
*/
if (xppl != null && xppl.red) {
// 祖父节点的左子节点设置为黑色
xppl.red = false;
// 祖父节点的右子节点也设置为黑色
xp.red = false;
// 祖父节点设置为红色
xpp.red = true;
// 暂存祖父节点,当前子树已经是红黑树结构
x = xpp;
}
else {
/** xpp
* \
* xp[红色]
* /
* [红]x
* 此种情况下需要执行右旋
*/
if (x == xp.left) {
root = TreeNode.rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
/** xpp
* \
* xp[红]
* \
* x[红]
* 原本是这种结构,或右旋之后变成这种结构,则需要执行左旋
*/
if (xp != null) {
// 父节点设置为黑色
xp.red = false;
if (xpp != null) {
// 祖父节点设置为红色
xpp.red = true;
root = TreeNode.rotateLeft(root, xpp);
}
}
}
}
}
}
// 红黑树左旋
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p) {
/**
* r 表示 right 右节点
* pp 表示 parent parent 祖父节点
* rl 表示 right left 右节点的左节点
* http://www.cnblogs.com/finite/p/8251587.html
*/
TreeNode<K,V> r, pp, rl;
if (p != null && (r = p.right) != null) {
if ((rl = p.right = r.left) != null) {
rl.parent = p;
}
if ((pp = r.parent = p.parent) == null) {
(root = r).red = false;
} else if (pp.left == p) {
pp.left = r;
} else {
pp.right = r;
}
r.left = p;
p.parent = r;
}
return root;
}
// 红黑树右旋
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
TreeNode<K,V> p) {
/**
* l 表示 left 左节点
* pp 表示 parent parent 祖父节点
* lr 表示 left right 左节点的右节点
* http://www.cnblogs.com/finite/p/8251587.html
*/
TreeNode<K,V> l, pp, lr;
if (p != null && (l = p.left) != null) {
if ((lr = p.left = l.right) != null) {
lr.parent = p;
}
if ((pp = l.parent = p.parent) == null) {
(root = l).red = false;
} else if (pp.right == p) {
pp.right = l;
} else {
pp.left = l;
}
l.right = p;
p.parent = l;
}
return root;
}
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
int n;
if (root != null && tab != null && (n = tab.length) > 0) {
// 读取 root 节点所在的 bucket 下标
final int index = n - 1 & root.hash;
// 读取 bucket 的首节点
final TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
// root 不是首节点,则需要执行变换
if (root != first) {
/**
* rn 表示 root next
* rp 表示 root previous
*/
Node<K,V> rn;
// 将 root 置为首节点
tab[index] = root;
// 读取 root 的前置节点
final TreeNode<K,V> rp = root.prev;
// 读取 root 的后置节点
if ((rn = root.next) != null) {
((TreeNode<K,V>)rn).prev = rp;
}
// root 的前置节点不为 null
if (rp != null) {
rp.next = rn;
}
// 首节点不为 null,则将其设置为 root 的前置节点
if (first != null) {
first.prev = root;
}
// root 的后置节点设置为首节点
root.next = first;
// root 的前置节点置为 null
root.prev = null;
}
assert TreeNode.checkInvariants(root);
}
}
/**
* Tree version of putVal.
*/
TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
int h, K k, V v) {
// 键比较器
Class<?> kc = null;
//
boolean searched = false;
// 读取根节点
final TreeNode<K,V> root = parent != null ? root() : this;
for (TreeNode<K,V> p = root;;) {
/**
* dir 表示 directory,-1 表示左子树,+1 表示右子树
* ph 表示 parent hash,父节点的哈希值
* pk 表示 parent key,父节点的键
*/
int dir, ph; K pk;
// 1)查找节点或新增节点位于左子树
if ((ph = p.hash) > h) {
dir = -1;
// 2)查找节点或新增节点位于右子树
} else if (ph < h) {
dir = 1;
// 3)节点已经存在,则直接返回
} else if ((pk = p.key) == k || k != null && k.equals(pk)) {
return p;
// 4)哈希值相等,但是键不相等【指定类型的多个子类对象的哈希值相等】
} else if (kc == null &&
(kc = HashMap.comparableClassFor(k)) == null ||
(dir = HashMap.compareComparables(kc, k, pk)) == 0) {
// 还没有搜索过子树
if (!searched) {
TreeNode<K,V> q, ch;
searched = true;
// 首先查找左子树,之后查找右子树
if ((ch = p.left) != null &&
(q = ch.find(h, k, kc)) != null ||
(ch = p.right) != null &&
(q = ch.find(h, k, kc)) != null) {
return q;
}
}
dir = TreeNode.tieBreakOrder(k, pk);
}
// 父节点更新为当前节点
final TreeNode<K,V> xp = p;
// 当前节点的左孩子或右孩子为空,表示可以直接插入
if ((p = dir <= 0 ? p.left : p.right) == null) {
// 读取后置节点
final Node<K,V> xpn = xp.next;
// 创建新的树节点,并维持单向链表结构
final TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
// 新节点是左孩子
if (dir <= 0) {
xp.left = x;
} else {
// 新节点是右孩子
xp.right = x;
}
// 更新后置节点
xp.next = x;
// 更新父节点
x.parent = x.prev = xp;
if (xpn != null) {
((TreeNode<K,V>)xpn).prev = x;
}
// 平衡红黑树并移动根节点
TreeNode.moveRootToFront(tab, TreeNode.balanceInsertion(root, x));
return null;
}
}
}
@Override
public V putIfAbsent(K key, V value) {
return putVal(HashMap.hash(key), key, value, true, true);
}
/**
* 根据键读取值,如果键不存在,则返回 null,否则返回映射的值
*/
@Override
public V get(Object key) {
Node<K,V> e;
return (e = getNode(HashMap.hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
/**
* tab:table
* e:element
* n:length
* k:key
*/
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
// 根据目标哈希定位到 bucket 不为 null
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[n - 1 & hash]) != null) {
// 1)首节点就是查找的目标节点,则直接返回
if (first.hash == hash && // always check first node
((k = first.key) == key || key != null && key.equals(k))) {
return first;
}
// 2)首节点不是目标节点,并且存在后置节点
if ((e = first.next) != null) {
// 2-1)如果首节点是红黑树节点
if (first instanceof TreeNode) {
// 则进行树节点查找
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
}
// 2-2)循环遍历单向链表查找目标节点
do {
if (e.hash == hash &&
((k = e.key) == key || key != null && key.equals(k))) {
// 找到则直接返回
return e;
}
} while ((e = e.next) != null);
}
}
// 未找到,则防护 null
return null;
}
TreeNode<K,V> getTreeNode(int h, Object k) {
return (parent != null ? root() : this).find(h, k, null);
}
/**
* 从根节点开始查找指定键
*/
TreeNode<K,V> find(int h, Object k, Class<?> kc) {
TreeNode<K,V> p = this;
do {
/**
* ph:parent hash
* dir:directory
* pk:parent key
* pl:parent left
* pr:parent right
*/
int ph, dir; K pk;
final TreeNode<K,V> pl = p.left, pr = p.right;
TreeNode<K,V> q;
if ((ph = p.hash) > h) {
// 1)parent 哈希值大于目标哈希,更新 parent 为左子节点
p = pl;
} else if (ph < h) {
// 2)parent 哈希值小于目标哈希,更新 parent 为右子节点
p = pr;
} else if ((pk = p.key) == k || k != null && k.equals(pk)) {
// 3)查找到目标节点,则直接返回
return p;
} else if (pl == null) {
// 4)哈希值相等但是键不相等,且左子节点为 null,则更新 parent 为右子节点
p = pr;
} else if (pr == null) {
// 5)哈希值相等但是键不相等,且右子节点为 null,则更新 parent 为左子节点
p = pl;
// 6)哈希值相等但是键不相等,并且左右子树都不为空。
} else if ((kc != null ||
(kc = HashMap.comparableClassFor(k)) != null) &&
(dir = HashMap.compareComparables(kc, k, pk)) != 0) {
// 更新 parent
p = dir < 0 ? pl : pr;
// 7)基于键比较器比较是相等的,尝试从右子树查找目标元素
} else if ((q = pr.find(h, k, kc)) != null) {
// 找到则直接返回
return q;
} else {
// 8)目标元素不存在右子树中,则更新 parent 为左子节点
p = pl;
}
// 只要 parent 不为 null, 则迭代遍历
} while (p != null);
// 目标键不存在,则返回 null
return null;
}
@Override
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
return (e = getNode(HashMap.hash(key), key)) == null ? defaultValue : e.value;
}
- 如果目标键存在,则进行值替换,并返回旧值,否则返回 null,注意旧值也可能是 null
@Override
public V replace(K key, V value) {
Node<K,V> e;
if ((e = getNode(HashMap.hash(key), key)) != null) {
final V oldValue = e.value;
e.value = value;
afterNodeAccess(e);
return oldValue;
}
return null;
}
- 如果存在指定的键值对,则将目标值替换为新值,替换成功返回 true,类似与 CAS 操作
@Override
public boolean replace(K key, V oldValue, V newValue) {
Node<K,V> e; V v;
if ((e = getNode(HashMap.hash(key), key)) != null &&
((v = e.value) == oldValue || v != null && v.equals(oldValue))) {
e.value = newValue;
afterNodeAccess(e);
return true;
}
return false;
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Node<K,V>[] tab;
if (function == null) {
throw new NullPointerException();
}
// 如果 HashMap 不为空,则进行逐个元素替换
if (size > 0 && (tab = table) != null) {
final int mc = modCount;
for (Node<K,V> e : tab) {
for (; e != null; e = e.next) {
// 通过函数式接口根基键和值计算新值
e.value = function.apply(e.key, e.value);
}
}
if (modCount != mc) {
throw new ConcurrentModificationException();
}
}
}
/**
* 移除 HashMap 中所有的键值对
*/
@Override
public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
// 重置计数值
size = 0;
// 将 bucket 的所有首节点置为 null
for (int i = 0; i < tab.length; ++i) {
tab[i] = null;
}
}
}
/**
* 返回键值对的总数
*/
@Override
public int size() {
return size;
}
/**
* HashMap 是否为空
*/
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean containsKey(Object key) {
return getNode(HashMap.hash(key), key) != null;
}
/**
* 是否包含指定的值
*/
@Override
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;
}
/**
* 1)如果指定的键值对不存在,则尝试新增。
* 2)如果存在,则使用函数值接口基于旧值和新值计算合并后的目标值
* 3)如果合并值为 null,则删除该节点。
*/
@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
if (value == null) {
throw new NullPointerException();
}
if (remappingFunction == null) {
throw new NullPointerException();
}
final int hash = HashMap.hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
// 容量超出阈值或未初始化,则执行扩容操作
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0) {
n = (tab = resize()).length;
}
// 读取首节点
if ((first = tab[i = n - 1 & hash]) != null) {
if (first instanceof TreeNode) {
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
} else {
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || key != null && key.equals(k))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
}
// 1)旧值不为 null
if (old != null) {
V v;
// 新值也不为 null
if (old.value != null) {
final int mc = modCount;
// 基于函数式接口计算合并值
v = remappingFunction.apply(old.value, value);
if (mc != modCount) {
throw new ConcurrentModificationException();
}
} else {
// 新值为 null,则使用旧值
v = value;
}
// 旧值或合并值不为 null
if (v != null) {
// 则更新旧值
old.value = v;
afterNodeAccess(old);
} else {
// 否则删除该节点
removeNode(hash, key, null, false, true);
}
return v;
}
// 2)旧值为 null,新值不为 null
if (value != null) {
// 2-1)如果是红黑树结构,则新增树节点
if (t != null) {
t.putTreeVal(this, tab, hash, key, value);
// 2-2)否则新增链表节点
} else {
tab[i] = newNode(hash, key, value, first);
// 单向链表元素个数超出树化阈值 8
if (binCount >= TREEIFY_THRESHOLD - 1) {
// 执行红黑树化操作
treeifyBin(tab, hash);
}
}
++modCount;
++size;
afterNodeInsertion(true);
}
return value;
}
/**
* 键存在,并且值不为 null,则根据函数式接口基于旧键和旧值计算新值,
* 1)新值不为 null,则更新值,并返回新值
* 2)新值为 null,则删除节点,并返回 null
* 键不存在或键关联的值为 null,则直接返回 null,无任何操作。
*/
@Override
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null) {
throw new NullPointerException();
}
Node<K,V> e; V oldValue;
final int hash = HashMap.hash(key);
// 键存在并且旧值不为 null
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
final int mc = modCount;
// 根据函数式接口基于旧键和旧值计算新值
final V v = remappingFunction.apply(key, oldValue);
if (mc != modCount) { throw new ConcurrentModificationException(); }
// 1)新值不为 null,则更新值
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
} else {
removeNode(hash, key, null, false, true);
}
}
return null;
}
/**
* 1)如果键值对已经存在,并且值不为 null,则不进行任何操作。
*/
@Override
public V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
if (mappingFunction == null) {
throw new NullPointerException();
}
final int hash = HashMap.hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0) {
n = (tab = resize()).length;
}
if ((first = tab[i = n - 1 & hash]) != null) {
if (first instanceof TreeNode) {
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
} else {
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || key != null && key.equals(k))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
V oldValue;
// 1)如果键值对已经存在,并且值不为 null,则不进行任何操作。
if (old != null && (oldValue = old.value) != null) {
afterNodeAccess(old);
return oldValue;
}
}
// 2)键不存在,或关联的值为 null
final int mc = modCount;
// 基于键计算新值
final V v = mappingFunction.apply(key);
if (mc != modCount) { throw new ConcurrentModificationException(); }
// 2-1)新值为 null,直接返回 null
if (v == null) {
return null;
// 2-2)键存在旧值为 null,新值不为 null,则更新旧值并返回新值。
} else if (old != null) {
old.value = v;
afterNodeAccess(old);
return v;
}
// 2-3)键不存在,新值不为 null,并且 bucket 为红黑树,则插入树节点
else if (t != null) {
t.putTreeVal(this, tab, hash, key, v);
} else {
// 2-4)键不存在,新值不为 null,并且 bucket 为链表,则插入链表节点
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1) {
treeifyBin(tab, hash);
}
}
modCount = mc + 1;
++size;
afterNodeInsertion(true);
return v;
}
/**
* 1)键存在,新值为不为 null,则更新值,新值为 null,则删除节点
* 2)键不存在,则执行插入操作
*/
@Override
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null) {
throw new NullPointerException();
}
final int hash = HashMap.hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0) {
n = (tab = resize()).length;
}
if ((first = tab[i = n - 1 & hash]) != null) {
if (first instanceof TreeNode) {
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
} else {
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || key != null && key.equals(k))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
}
// 键不存在或旧值为 null 时,oldValue 为 null
final V oldValue = old == null ? null : old.value;
final int mc = modCount;
final V v = remappingFunction.apply(key, oldValue);
if (mc != modCount) { throw new ConcurrentModificationException(); }
// 1)键已经存在
if (old != null) {
// 1-1)新计算值不为 null,则更新值
if (v != null) {
old.value = v;
afterNodeAccess(old);
// 1-2)新计算值为 null,则删除节点
} else {
removeNode(hash, key, null, false, true);
}
}
// 2)键不存在
else if (v != null) {
// 2-1)如果目标 bucket 是红黑树,则执行树节点插入
if (t != null) {
t.putTreeVal(this, tab, hash, key, v);
// 2-2)执行链表节点插入
} else {
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1) {
treeifyBin(tab, hash);
}
}
modCount = mc + 1;
++size;
afterNodeInsertion(true);
}
return v;
}