JDK8中的HashMap的resize()方法,如果修改了load factor,可能会出现误差?

问题描述

JDK8中的JDK8中的HashMap的resize()方法,如果修改了loadFactor,在之后resize的时候可能会出现误差。

按理说,threshold应该一直等于 capacity * loadFactor,但是resize()方法中,当capacity大于16之后,在把capacity变为原来的两倍的同时,把threshold也直接变为了原来的两倍了。这种方式在loadFactor为默认值,也就是0.75的时候是没有问题的。但是如果自己调整了loadFactor,比如把loadFactor改了0.70,那么这种扩容方式就会出现误差。

如图
图片描述

图片描述

我已经通过调试测试验证了以上数据。想问一下,有没有人知道类的设计者为什么没有考虑这种情况,还是有意忽略了?

相关代码

resize()函数部分源代码

// 进入这个方法的前提代表size已经大于threshold了

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) { // oldCap大于0,说明table长度不是0,不是第一次resize()

if (oldCap >= MAXIMUM_CAPACITY) { // oldCap存的是table长度,就是扩容前的容量,大于等于最大capacity,不再扩容

threshold = Integer.MAX_VALUE; // 把threshold设置为整形的最大值,这样之后就不会再进入resize()方法了

return oldTab;

}

else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && // 需满足扩容之后仍小于最大capacity限制

oldCap >= DEFAULT_INITIAL_CAPACITY) // 需满足旧的容量大于默认初始容量

newThr = oldThr << 1; // double threshold // 产生误差的地方,因为这个时候旧的threshold直接乘2,

// 不再是新容量乘以负载因子了,误差会不断累积

}

// 只有初始化的时候才会进入

else if (oldThr > 0) // initial capacity was placed in threshold 这里只针对显式初始化了initial capacity的情形,此时

// oldCap等于零,说明oldTab为null。oldThr存的是初始化的threshold,也就是initial capacity

newCap = oldThr; // 这里就相当于把newCap赋值为initial capacity了

// 没有显式指定initial capacity,或者显式制定了initial capacity为0,都使用默认initial capacity

else { // zero initial threshold signifies using defaults,

newCap = DEFAULT_INITIAL_CAPACITY;

newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

}

// 指定的initial capacity小于默认capacity时候的前几次resize()

// 所有情形最后一次resize()

if (newThr == 0) {

float ft = (float)newCap * loadFactor;

newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?

(int)ft : Integer.MAX_VALUE);

}

threshold = newThr;

回答:

虽然误差的绝对值在变大,但是比例却始终很小

以上是 JDK8中的HashMap的resize()方法,如果修改了load factor,可能会出现误差? 的全部内容, 来源链接: utcz.com/a/166308.html

回到顶部