HashMap 的属性
/**
* 默认的初始容量为 16
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
/**
* 最大的容量 = 2^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;
/**
* 最小树化的桶容量
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* 数组,又叫作桶(bucket),存储 map 中数据
*/
transient Node<K,V>[] table;
/**
* 作为entrySet()的缓存
*/
transient Set<Map.Entry<K,V>> entrySet;
/**
* 元素的数量
*/
transient int size;
/**
* 修改次数,用于在迭代的时候执行快速失败策略
*/
transient int modCount;
/**
* map 扩容的阈值:size > threshold,map 自动扩容,threshold = capacity * loadFactor
*/
int threshold;
/**
* 装载因子
*/
final float loadFactor;
java.util.HashMap#tableSizeFor 方法
/**
*
* 该方法是为了 map 在初始化或扩容时,容量取离 cap 最近的 2^n 的值
*
*/
static final int tableSizeFor(int cap) {
// 防止在 cap 已经是 2^n 的情况下,经过运算后得到的结果是 cap * 2
// 例如 n = l6(0001 0000),经过下面运算之后,n = 0001 1111
// 此时最后一步 n + 1 执行之后,此时的值就不是里 cap 最近的 2^n(16) 的值,而是 cap * 2(32)
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
// 如果 n 小于 0,返回 1,如果 n 大于最大容量,将不会再扩容,否则返回 n + 1
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
java.util.HashMap#put 方法
/**
* 将指定值与 map 中的指定键关联。如果 key 存在,value 替换原来的值
*
* @param key 指定的 key
* @param value 指定 key 的值
* @return 如果 key 存在,返回之前的值,如果 key 不存在,则返回null。
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
java.util.HashMap#putVal 方法
/**
* 实现 map 和相关方法.
*
* @param hash key 的 hash 值
* @param key 插入 map 的 key
* @param value 值
* @param onlyIfAbsent 如果为 true,不改变之前 key 的值
* @param evict 如果为 false,则 table 处于 creation mode。
* 该值对 HashMap 没有作用,对 LinkedHashMap 有作用
* @return 返回之前 key 的值,如果没有返回 null
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
// tab:当前 map 的值,p:key 对应桶的位置存储的值(链表或树)
// n:map.table 的长度,i:key 对应桶的索引值
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
// 如果桶为 null 或桶的大小为 0,初始化 map
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
// 如果 key 对应的桶为 null,新建一个节点放在桶中
tab[i] = newNode(hash, key, value, null);
else {
// key 对应的桶不为 null
// e 记录已存在 key 的 node
// k 记录 e.key
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)
// 桶中的元素类型是 TreeNode (红黑树)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 桶中元素类型为 Node (链表)
// binCount记录遍历链表节点的数量
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
// 找到链表尾部,并把新插入的节点放到最后
p.next = newNode(hash, key, value, null);
// 新增节点之后,需要判断该桶中元素数量是否达到树化的阈值
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
// binCount 大于等于树化的阈值,需要树化
treeifyBin(tab, hash);
break;
}
// 如果待插入的 key 在链表中存在了,则退出循环
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // map 中存在 key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
// 如果之前的值为 null 或者 onlyIfAbsent 为 false,替换旧值
e.value = value;
// HashMap 中未实现该方法
afterNodeAccess(e);
// 返回之前的值
return oldValue;
}
}
++modCount;
if (++size > threshold)
// 元素大小大于阈值时,自动扩容
resize();
// HashMap 中未实现该方法
afterNodeInsertion(evict);
return null;
}
/**
* 获取到 key 的 hash 值 无符号右移 16 位并和自己进行异或运算,
* 这样做的目的是为了让高位与低进行混合,让两者都参与运算
* 以便让 hash 值分布更加均匀、分散
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
java.util.HashMap#resize 方法
/**
*
* 初始化或两倍增加 table 的大小。
*
* @return the table
*/
final Node<K,V>[] resize() {
// oldTab-旧数组,oldCap-旧 table 容量,oldThr-旧扩容阈值
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
// newCap-新容量 newThr-新扩容阈值
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
// 如果旧容量达到了 MAXIMUM_CAPACITY,则不再进行扩容
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
// 如果 oldCap << 1 小于最大容量
// 并且 oldCap 大于 DEFAULT_INITIAL_CAPACITY
// 则容量扩大为两部,扩容门槛也扩大为两倍
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0)
// 使用非默认构造方法创建的 map,第一次插入元素会走到这里
// oldCap == 0 且 oldThr > 0
// 把 newCap 设为 oldThr
newCap = oldThr;
else {
// 调用默认构造方法创建的 map,第一次插入元素会走到这里
// oldCap == 0 且 oldThr >== 0,说明还未初始化过
// 则 newCap 为默认容量,newThr 为默认容量 * 默认装载因子
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
// 未对 newThr 设置,需要初始化 newThr
// 如果 newCap 和 newCap * loadFactor 都小于 MAXIMUM_CAPACITY
// 此时 newThr = newCap * loadFactor,否则等于 Integer.MAX_VALUE
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
// 重新设置 threshold 大小
threshold = newThr;
// 新 table 的大小为 newCap
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
// 重新设置 table 为 newTab
table = newTab;
if (oldTab != null) {
// 旧 table 不为空,需要将之前的值迁移到新 table 中,重新计算索引值
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(红黑树)
// 把这颗树打散成两颗树插入到新桶中去
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else {
// 桶中的元素类型为 Node(链表),且链表的大小大于 2
// 分化成两个链表插入到新的桶中去
// 如:oldCap = 4,3、7、11、15 这四个元素都在 3 桶中
// newCap = 8,则 3、11 还是在 3 号桶 lo
// 7、15 要搬移到 7 桶中去 hi
// 分化成了两个链表 lo 和 hi
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) {
// 元素在旧桶中的位置
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;
// 高位链表在新桶中的位置正好是原来的位置加上 oldCap
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
java.util.HashMap.TreeNode#putTreeVal 方法
/**
* Tree version of putVal.
*/
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
int h, K k, V v) {
// key 的比较器
Class<?> kc = null;
// 标记是否检索到 key 的节点
boolean searched = false;
// 设置树的根节点
TreeNode<K,V> root = (parent != null) ? root() : this;
// 从树的根节点开始遍历
for (TreeNode<K,V> p = root;;) {
// dir 标记是在左边还是右边,-1 左边,1 右边
// ph = p.hash,当前节点 p 的hash
int dir, ph;
// pk = p.key,当前节点 p 的key
K pk;
if ((ph = p.hash) > h)
// h < p.hash,在左边
dir = -1;
else if (ph < h)
// h > p.hash,在右边
dir = 1;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
// p.hash == h && key 相等,说明 key 已存在,直接返回该节点
// 回到 putVal() 中判断是否需要修改其 value 值
return p;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
if (!searched) {
// 以上未找到 k
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;
}
// 如果两者类型相同,再根据它们的内存地址计算hash值进行比较
dir = tieBreakOrder(k, pk);
}
TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
Node<K,V> xpn = xp.next;
// 如果最后确实没找到对应key的元素,则新建一个节点
TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
// 根据 dir 判断插入位置
if (dir <= 0)
xp.left = x;
else
xp.right = x;
// 设置当前节点 p 的 next 指针的值为 x
xp.next = x;
// 设置新节点 x 的父节点为 xp
// 设置新节点 x 的 pre 指针的值为 xp
x.parent = x.prev = xp;
if (xpn != null)
// 当前节点 p 的 next 不(xpn)为空,需要将 xpn 的 pre 指针设为新节点 x
((TreeNode<K,V>)xpn).prev = x;
// 插入后,调用 balanceInsertion 方法,平衡红黑树
// 把平衡后的 root 节点移动到链表的第一个节点
moveRootToFront(tab, balanceInsertion(root, x));
// 跳出循环,返回
return null;
}
}
}