1、HashMap为什么是无序的?
HashMap源码629行
if ((p = tab[i = (n - 1) & hash]) == null)
n:数组长度
hash:元素hash值 tips:Integer类型的hash值是它本身
tab:数组
i =(n - 1) & hash :如果n=16(HashMap默认值) 则 i 的取值范围为[0,15]
所以说 如果当前hash=16 则i = 0;则p被放入 tab[0]的链表或红黑树节点中
2、HashMap中转成红黑树的触发条件?
HashMap源码639-650行
//遍历数组链表节点
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//触发treeifyBin方法 当binCount=7时,也就是说当前元素为第8个时进入该方法
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
当链表上有8个元素时进入treeifyBin方法
//treeifyBin方法中 如果数组的长度小于64时。则进扩容方法resize()
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
总结:
链表节点转树节点的触发条件是a&&b
a、数组表的长度大于64
b、链表上的节点 >= 8
3、HashMap数组怎么扩容?
先看扩容的条件:
1、数组为空
//627-628
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
2、链表节点>=8 && 数组小于64
//756-757
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
3、数组总元素 > 容量负载临界点
//661-662
//size 总元素个数永远自增长
//threshold The next size value at which to resize (capacity * load factor).
if (++size > threshold)
resize();
第一次 临界点 = 数组长度 * 负载因子(0.75) = 16 * 0.75 = 12
第二次 临界点 = 临界点 << 2= 容量负载因子*2
第n次:当容量大于( MAXIMUM_CAPACITY = 1 << 30 = 2^30 )时:临界点的值为int最大值 2^31-1
数组长度范围[16,2^31-1]
再看扩容后的节点安排
步骤:
1、循环数组
2、判断当前下标是否有值(无值则跳过,以下说明是有值的情况)
3、判断当前下标Node节点,的下一个Node节点是否有值
无值则直接添加到数组内, 数组下标 = 当前hash值 & 最大容量-1
有值则进入第4点
4、判断是否为树
true:分裂当前树
5、重新计算所有节点,放入数组中
计算条件
//当前hash 与 旧容器进行 & 运算
if ((e.hash & oldCap) == 0) {}
如果是0放入原来下标位置
如果非0则放入j+ oldCap (当前遍历的下标 + 旧容器大小)
tips:709行置空 目前猜测目的是:1、释放索引方便垃圾回收器回收 2、方便分裂树时,减少遍历
oldTab[j] = null;