目录
1 成员变量
/// 类属性
// 默认初始化容量大小
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 默认最大容量大小 超过此值的容量 均设为此容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认负载因子 put时 size()> capacity* 负载因子时 扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 联表节点个数超过8时 联表转为红黑树
static final int TREEIFY_THRESHOLD = 8;
// 红黑树节点个数小于6时 退化为链表 提高插入删除性能
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;
// 更改次数
transient int modCount;
// 下次扩容时的阈值 还未put时 设置为 集合容量 否则为 cap* loadFactor
int threshold;
// 负载因子
final float loadFactor;
2 静态内部类
// 节点模型类
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V 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; }
// 重写toString 方便打log
public final String toString() { return key + "=" + value; }
// 重写hashCode 方法 hashcode 由 key和value 值共同组成
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
// 重写equals 方法 key相等 且value相等 或同一对象 则认为是equals
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
3 构造方法
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
// 参数校验
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// 初始化负载因子
this.loadFactor = loadFactor;
// 初始化下次扩容阈值 tableSizeFor 将当前值 转为 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
}
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
4 tableSizeFor
cap 小于等于1<<30;
先来假设n的二进制为0001 0000 0000 0000 0000 0000 0000 0001。接着 cap-1:0001 0000 0000 0000 0000 0000 0000 0000
对n右移1位:0000 1000 0000 0000 0000 0000 0000 0000,再位或:0001 1000 0000 0000 0000 0000 0000 0000
对n右移2为:0000 0110 0000 0000 0000 0000 0000 0000,再位或:0001 1110 0000 0000 0000 0000 0000 0000
此时前面已经有四个1了,再右移4位且位或可得8个1: 0001 1111 1110 0000 0000 0000 0000 0000
同理,有8个1,右移8位肯定会让后八位也为1。0001 1111 1111 1111 1110 0000 0000 0000
综上可得,该算法让最高位的1后面的位全变为1。0001 1111 1111 1111 1111 1111 1111 1111
最后再让结果n+1,即得到了2的整数次幂的值了。0010 0000 0000 0000 0000 0000 0000 0000
5 put/putVal
public V put(K key, V value) {
// onlyIfAbsent为false,说明put方法会覆盖之前的值
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 如果table为null,先调用resize()方法分配空间
if ((tab = table) == null || (n = tab.length) == 0)
// 初始化之后的容量
n = (tab = resize()).length;
//没有hash冲突,直接存入
if ((p = tab[i = (n - 1) & hash]) == null)
// node中 缓存hash值 用于 比较
tab[i] = newNode(hash, key, value, null);
// 存在hash冲突 拉链法 树法解决哈希冲突
else {
Node<K,V> e; K k;
// 如果头结点与待插入节点 哈希值相等 且 key相同 头结点设置为旧元素节点
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 如果 头结点是 树节点 调用树的插入方法
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
// 执行链表插入
else {
// binCount 记录当前节点 元素个数
for (int binCount = 0; ; ++binCount) {
//如果p是链表最后一个节点,e = null,p链接到新的节点,如果节点数大于或等于8个,链表转换成红黑树,结束
if ((e = p.next) == null) {
// 尾插法 插入时间复杂度O(n) 解决原哈希扩容死循环问题
p.next = newNode(hash, key, value, null);
// 当前节点元素个数超过树化阈值 转化为树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//如果找到e不是最后一个节点,且e.key equals key, hash加速判断效率 则说明需要覆盖e的value,跳出循环到下一步
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
// 循环下一个节点
p = e;
}
}
// 有重复key 更新value 并返回旧value
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e); //子类LinkedHashMap实现的空方法
return oldValue;
}
}
++modCount;
//当前数组大小超过阈值,扩容 需要重新分配空间,rehash已有数据
if (++size > threshold)
resize();
afterNodeInsertion(evict);
// 无重复key 新插入节点 返回旧值为null
return null;
}
6 resize
调用put方法时,或者用已有map初始化时构造方法时,如果map是空的,先调用resize方法。put方法后,如果size > threshold,再调用一次resize方法
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
// 旧的map容量
int oldCap = (oldTab == null) ? 0 : oldTab.length;
// 旧map阈值
int oldThr = threshold;
int newCap, newThr = 0;
// 旧容器已初始化,则新分配的容量为两倍,新的阈值为原来的两倍
if (oldCap > 0) {
// 如旧容量达到最大限制 设置阈值为最大值 不再执行扩容
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 新容量未超过最大容量 且 旧容量大于等于16 设置新阈值为旧阈值的2倍
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 threshold
newCap = oldThr;
// 容器未初始化 且阈值未初始化 容量设置为默认初始容量 阈值设置为 默认 负载因子* 默认初始容量
else { // zero initial threshold signifies using defaults
newCap = 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;
// 旧容器不为null 数据迁移到新容器
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 order
Node<K,V> loHead = null, loTail = null; // 最低桶位置的链表 首尾节点
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
// 记录下次遍历节点
next = e.next;
//oldCap是2的整次方,如果e在oldCap的二进制那个1的位置是0,说明当newCap = oldCap << 1 或oldCap时,e的位置无变化
if ((e.hash & oldCap) == 0) {
// e的位置无变化 e&(newCap - 1) == e&(oldCap - 1) ,用loHead将相同的e连接成单链表
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
//oldCap是2的整次方,如果e在oldCap的二进制那个1的位置是1,说明当newCap = oldCap << 1,
// e&(newCap - 1) = e&(oldCap - 1)|oldCap,即rehash后的位置是当前位置加上oldCap,用hiHead将这样的e连接起来
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
//newTab[j]存放的是原数组中位置不变的元素
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
//newTab[j+oldCap]存放的是原数组中位置加oldCap的元素
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
7 get/getNode
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value; //先将key进行hash,然后调用getNode方法
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
// 容器已初始化 且容器有元素 且 目标位置头结点不为null 执行 链表或 树查找逻辑
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// 如头结点 key equals 返回头结点
if (first.hash == hash && // always check first node
((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);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
// 无此key元素 返回null
return null;
}
8 remove/removeNode
public V remove(Object key) {
Node<K,V> e;
//matchValue为false表示不匹配value值
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.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;
// 桶位置头结点不为null
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;
// 1 查找key对应的节点
// 头结点 key equals
if (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);
}
}
// 2 执行remove
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;
// 待删除的为链表普通结点
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
// 未命中 返回null
return null;
}