分析版本是JDK 1.8, 包含红黑树的部分解析,几乎每一行都有注释,欢迎沟通交流。
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;//获取存放数据的数组
int oldCap = (oldTab == null) ? 0 : oldTab.length;//数组长度,也叫成为容量,不同于初始化容量initialCapacity < length(被拓展至2的幂次方 )
/*左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,但是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就需要遍历这个链表,得到8或者9,这样就降低了查询的效率。同时,我们也可以发现,当数组长度为15的时候,hashcode的值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!*/
int oldThr = threshold;//阈值:容量*负载因子(0.75)
int newCap, newThr = 0;
if (oldCap > 0) {
//只有非第一次扩容才会进来(第一次扩容在第一次put,table还没初始化,oldCap = 0)
if (oldCap >= MAXIMUM_CAPACITY) {
//已经扩容到最大容量
threshold = Integer.MAX_VALUE;//调高阈值
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)//扩容2倍,并且设置为新的容量
//容量较小时 capacity * loadFactor造成的误差比较大,也就是oldCap对应的oldThr误差比较大,当oldCap<16时,不通过扩大2倍计算新阈值newThr
//通过capacity * loadFactor计算还是位运算的权衡
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
// oldCap = 0 而 oldThr > 0,表示构造Map时带有阈值参数,oldThr初始化计算方法时tableSizeFor(initialCapacity)
newCap = oldThr;//第一次初始化时oldThr实际含义是容量,在此是借壳初始化
else {
// zero initial threshold signifies using defaults
// oldCap = 0 而 oldThr <= 0 无参初始化,设置为默认值
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//到此,newThr已经是最终值,但是newThr在带参初始化或者原容量比较小时,还未初始化
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;//使用一个新的数组代替已有的容量小的数组
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
//数组该位置是一个链表
// 之所以定义两个头两个尾对象,是由于链表中的元素的下标在扩容后,要么是原下标+oldCap,要么不变
//因为oldTab的元素下标是根据 hash(key) & (oldCap-1) 计算的,如果扩容后,计算下标是 hash(key) & (2*oldCap-1)
//扩容后的下标取决于hash(key)的二进制对应(2*oldCap-1)的高位是0还是1,而oldCap就是(2*oldCap-1)的高位
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) {
//与高位取与为0,表明下标没有改变,设置为低位链表loHead和loTail
if (loTail == null)
//若尾节点为空,表明是第一个节点,所以只是为头节点,记录头节点
loHead = e;
else
//尾节点不为空,表明已经添加了节点,将其放在尾节点后面
loTail.next = e;
loTail = e;//始终添加的节点是尾节点
}
else {
//与高位取与为1,表明下标会改变,设置为高位链表hiHead和hiTail
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) {
//插入到更新后的位置
//由于0
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
红黑树操作:
/**
* Splits nodes in a tree bin into lower and upper tree bins,
* or untreeifies if now too small. Called only from resize;
* see above discussion about split bits and indices.
*
* @param map the map
* @param tab the table for recording bin heads
* @param index the index of the table being split
* @param bit the bit of hash to split on
*/
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
//map: HashMap对象
//tab:新的Node[]
//index:分析HashMap对象中的节点编号
//bit: oldCap原来的HashMap对象容量大小
TreeNode<K,V> b = this;//当前对象是原HashMap对象Node[index]这个节点
// Relink into lo and hi lists, preserving order
TreeNode<K,V> loHead = null, loTail = null;
TreeNode<K,V> hiHead = null, hiTail = null;
int lc = 0, hc = 0;
for (TreeNode<K,V> e = b, next; e != null; e = next) {
//双向链表遍历红黑树,拆分成为两个双向链表
next = (TreeNode<K,V>)e.next;
e.next = null;
if ((e.hash & bit) == 0) {
//与高位取与为0,表明下标没有改变
if ((e.prev = loTail) == null)
loHead = e;
else
loTail.next = e;
loTail = e;
++lc;
}
else {
//与高位取与为1,表明下标会改变
if ((e.prev = hiTail) == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
++hc;
}
}
if (loHead != null) {
if (lc <= UNTREEIFY_THRESHOLD)
//节点数不够红黑树个数,转化为链式的
tab[index] = loHead.untreeify(map);
else {
tab[index] = loHead;//构建的红黑树双向链表赋值给新的Node[]
if (hiHead != null) // (else is already treeified)
//构建红黑树
loHead.treeify(tab);
}
}
if (hiHead != null) {
if (hc <= UNTREEIFY_THRESHOLD)
tab[index + bit] = hiHead.untreeify(map);
else {
tab[index + bit] = hiHead;
if (loHead != null)
hiHead.treeify(tab);
}
}
}
若拆分后红黑树节点数不够,会转化为链式,需要切换对象类型
/**
* Returns a list of non-TreeNodes replacing those linked from
* this node.
*/
final Node<K,V> untreeify(HashMap<K,V> map) {
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = this; q != null; q = q.next) {
Node<K,V> p = map.replacementNode(q, null);
if (tl == null)
hd = p;
else
tl.next = p;
tl = p;
}
return hd;
}
若拆分后依旧能构成红黑树,需要将在双向链表结构基础上补充红黑树
/**
* Forms tree of the nodes linked from this node.
* @return root of tree
*/
final 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;//保留链式结构的下一个节点
x.left = x.right = null;
if (root == null) {
x.parent = null;
x.red = false;
root = x;//设置根节点
}
else {
K k = x.key;
int h = x.hash;
Class<?> kc = null;
for (TreeNode<K,V> p = root;;) {
//在已有的红黑树结构上插入下的节点
int dir, ph;
K pk = p.key;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
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 = balanceInsertion(root, x);
break;
}
}
}
}
moveRootToFront(tab, root);
}