接上几节,我们分析了hashmap在put一个数据时,会发生的情况:
1、根据key,计算hash,确定下标;
2、当第一次put时,如果table为空,则会初始化table数组,长度为16;
2、当table内,无数据时,put的数据将直接tab[下标]=newNode();
3、当有数据时:
3.1、当来源是同一个key时,则会将key设置成最后一次的put的value;
3.2、当来源是一个新的时,则遍历节点,知道最后一个为null,这时会把node.next=newNode()进行赋值;
3.3、当节点中,局部变量等于8-1时,则会将该链表进行treeifyBin()(暂时没讲)
4、增加数据后,判断++size > threshold,如果大于,则执行行resize()即动态扩容(暂时没讲)
那么接下来,就看看treeifyBin()和resize()方法;
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
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);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
我们说,tab的size等于8-1的时候才执行,可以看出,这个tab是一个很长的链表;
static final int MIN_TREEIFY_CAPACITY = 64;
可以看到,它会走到resize()方法。很奇怪,成员变量table的某个下标的长度大于等于8时,就进行resize()了,并不是我们说的达到table的0.75倍,它才resize();看看这个resize()究竟是什么;
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)
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;
if (oldTab != null) {
...
}
return newTab;
}
很长,我们省略了一部分;
下面我们看下条件继续走
int oldCap=16;
int oldThr = threshold=16;
int newCap, newThr = 0;
static final int MAXIMUM_CAPACITY = 1 << 30;=1073741824
这里当然不会走: threshold = Integer.MAX_VALUE;
走到判断: else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
16 << 1
=10000 << 1 (十进制16,它的二进制是10000)
=100000 (二进制100000,它的十进制是32)
=32
(大家看我写的这个等式,是不是有点眼熟,像不像小学的时候,做的加减乘除写的计算过程?)
newCap=32;它是小于1 << 30
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
并且它是大于16的,满足条件
即:newThr = oldThr << 1;
newThr = 16 << 1
newThr = 32
if (oldCap > 0) {}这里面的else if else if 都是为了处理新节点数组的长度即长度左移一位,从值上看,就是长度乘以2;
继续往下,我们看了新建note数组了:
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
现在table的长度就是32了,那原来的table上面的数据已经赋值给了oldTab,那数据都在oldTab,下面,它肯定是要将上面的数据,重新插入到新的table数据上了;
继续往下看,oldTab肯定不为空,则往下,来到for循环,肯定是节点遍历了:
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;
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;
newTab[j + oldCap] = hiHead;
}
}
}
}
代码有很多;这里我们假设,原来老tab中 ,只有下标为11的,有1条链表,长度为8;
它这里通过e= oldTab[j],然后再oldTab[j] = null;
明天我们再分析;