1. HashMap 简介
相比 LinkedList 和 ArrayList,HashMap 的源码需要化更多的时间来学习,实现思路也很巧妙。
HashMap 采用 key/value 结构存储,即为键值对,每个 key 对应唯一的 value。
2. 属性
/**
* 默认初始容量16
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* 最大容量 2^30,如果需要指定,必须为2的幂
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认负载因子 0.75
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 树化条件,一个桶中的元素个数大于8时树化
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 桶中元素个数小于6时逆树化
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 当桶的个数大于64时才树化
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* 数组(桶)
*/
transient Node<K,V>[] table;
/**
* entrySet 的缓存
*/
transient Set<Map.Entry<K,V>> entrySet;
/**
* 元素个数
*/
transient int size;
/**
* 移动次数,用于在迭代时执行快速失败策略
*/
transient int modCount;
/**
* 当桶的个数达到 (容量 * 负载因子 时进行扩容)
*/
int threshold;
/**
* 负载因子
*/
final float loadFactor;
3. Node 节点定义
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
// 红黑树
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;
}
4. 构造函数
/**
* 指定初始容量和负载因子的构造函数
*/
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;
// 扩容门槛
this.threshold = tableSizeFor(initialCapacity);
}
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
// 扩容门槛为初始容量向上取最近的2的n次方
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;
}
/**
* 指定初始容量的HashMap
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 空参构造函数
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
* 将 m Map复制到HashMap
*/
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
5. 主要方法
/**
* 如果Map中存在,替换旧值
*/
public V put(K key, V value) {
// 通过 hash 函数计算 key 的哈希值
return putVal(hash(key), key, value, false, true);
}
/**
* 计算哈希值
*/
static final int hash(Object key) {
int h;
// 如果 key 为空,则哈希值为0,否则将哈希值与高16位进行异或,再哈希,使得哈希值更分散
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 如果桶(数组)为空或个数为0,初始化数组
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// (n - 1) & hash 计算元素在哪个桶中
// 如果桶为空,则添加到第一个元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
// 桶中有元素
Node<K,V> e; K k;
// 如果哈希值相等,并且桶中第一个元素的key与带插入元素的key值相等,保存到e中方便后序修改value
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 如果桶中的第一个元素是树节点,调用 putTreeVal 插入
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 遍历桶对应的链表
for (int binCount = 0; ; ++binCount) {
// 如果链表遍历完了都没有找到相同key的元素,则在链表的最后插入一个新的元素
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// 如果插入之后链表元素个数大于8,则需要树化
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// 如果遍历时找到了相同key的元素,中断循环
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
// 存在相同key的元素
if (e != null) { // existing mapping for key
// 记录旧值
V oldValue = e.value;
// 判断是否需要替换旧值
if (!onlyIfAbsent || oldValue == null)
e.value = value;
// 节点被访问后调用该方法
afterNodeAccess(e);
return oldValue;
}
}
// 查找失败,修改次数+1
++modCount;
// 元素个数+1,检查是否需要扩容
if (++size > threshold)
resize();
// 节点插入之后调用
afterNodeInsertion(evict);
return null;
}
// 下面两个方法在LinkedList中重写
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
/**
* 初始化或加倍表大小。
*
* @return the table
*/
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) {
// 如果旧容量大于最大容量
if (oldCap >= MAXIMUM_CAPACITY) {
// 扩容门槛设置为最大整数,即不再进行扩容
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 如果旧容量的两倍小于最大容量,且旧容量>=默认初始化容量16
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
// 如果就扩容门槛大于0,赋值给新容量
newCap = oldThr;
else { // zero initial threshold signifies using defaults
// 否则新容量为默认初始化容量,新扩容门槛为 16 * 0.75
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 = newTab;
// 遍历旧数组
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
// 旧数组元素复制到新数组
if ((e = oldTab[j]) != null) {
// 旧数组置空,方便GC回收
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
// 如果这个链表不止一个元素且不是一颗树
// 则分化成两个链表插入到新的桶中去
// 比如,假如原来容量为4,3、7、11、15这四个元素都在三号桶中
// 现在扩容到8,则3和11还是在三号桶,7和15要搬移到七号桶中去
// 也就是分化成了两个链表
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
// (e.hash & oldCap) == 0的元素放在低位链表中
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
// (e.hash & oldCap) != 0的元素放在高位链表中
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;
}
/**
* 扩容方法
*/
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) {
if (oldCap >= MAXIMUM_CAPACITY) {
// 如果旧容量达到了最大容量,则不再进行扩容
threshold = Integer.MAX_VALUE;
return oldTab;
} else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
// 如果旧容量的两倍小于最大容量并且旧容量大于默认初始容量(16),则容量扩大为两部
// 扩容门槛也扩大为两倍
newThr = oldThr << 1; // double threshold
} else if (oldThr > 0) // initial capacity was placed in threshold
// 使用非默认构造方法创建的map,第一次插入元素会走到这里
// 如果旧容量为0且旧扩容门槛大于0,则把新容量赋值为旧门槛
newCap = oldThr;
else { // zero initial threshold signifies using defaults
// 调用默认构造方法创建的map,第一次插入元素会走到这里
// 如果旧容量旧扩容门槛都是0,说明还未初始化过,则初始化容量为默认容量,
// 扩容门槛为默认容量*默认装载因子
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 = newTab;
// 如果旧数组不为空,则搬移元素
if (oldTab != null) {
// 遍历旧数组
for (int j = 0; j < oldCap; ++j) {
Node<K, V> e;
// 如果桶中第一个元素不为空,赋值给e
if ((e = oldTab[j]) != null) {
// 清空旧桶,便于GC回收
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;
// (e.hash & oldCap) == 0的元素放在低位链表中
// 比如,3 & 4 == 0
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
} else {
// (e.hash & oldCap) != 0的元素放在高位链表中
// 比如,7 & 4 != 0
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;
}