不管是JDK1.7或者JDK1.8 当put方法执行的时候,如果table为空,则执行resize();方法扩容。默认长度为16;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
- JDK1.7 是先扩容,在添加。具体put是否扩容需要两个条件:
1、 存放新值的时候当前已有元素的个数必须大于等于阈值
2、 存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)
扩容方法是在addEntry方法中
void addEntry(int hash, K key, V value, int bucketIndex) {
//1、判断当前个数是否大于等于阈值
//2、当前存放是否发生哈希碰撞
//如果上面两个条件否发生,那么就扩容
if ((size >= threshold) && (null != table[bucketIndex])) {
//扩容,并且把原来数组中的元素重新放到新数组中
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
扩容过程中会将原来的数据,放入到新的数组中,但是会重新计算hash值进行分配
因为上面这两个条件,所以存在下面这些情况
(1)、就是hashmap在存值的时候(默认大小为16,负载因子0.75,阈值12),可能达到最后存满16个值的时候,再存入第17个值才会发生扩容现象,因为前16个值,每个值在底层数组中分别占据一个位置,并没有发生hash碰撞。
(2)、当然也有可能存储更多值(超多16个值,最多可以存26个值)都还没有扩容。原理:前11个值全部hash碰撞,存到数组的同一个位置(这时元素个数小于阈值12,不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生hash碰撞,所以不会扩容),前面11+15=26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象。
- JDK1.8 是先添加,在扩容。具体put是否扩容需要满足一个条件:
当table中存储值的个数大于等于threshold的时候,进行扩容。容量为原来的2倍。
if (++size > threshold)
resize();
当table扩容后的table大小变为原来的两倍,接下来就是进行扩容后table的调整:
假设扩容前的table大小为2的N次方,元素的table索引为其hash值的后N位确定,那么扩容后的table大小即为2的N+1次方,则其中元素的table索引为其hash值的后N+1位确定,比原来多了一位
因此,table中的元素只有两种情况:
- 元素hash值第N+1位为0:不需要进行位置调整
- 元素hash值第N+1位为1:调整至原索引的两倍位置
在resize方法中,第45行的判断即用于确定元素hashi值第N+1位是否为0:
- 若为0,则使用loHead与loTail,将元素移至新table的原索引处
- 若不为0,则使用hiHead与hiHead,将元素移至新table的两倍索引处
红黑树转化条件:数组的长度大于64的时候,链表长度大于8才会从链表转换为红黑树