从JDK8开始,HashMap采用数组+链表+红黑树的方式存储数据。
HashMap的CAPACITY默认值是16,参考源码:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
FACTOR默认值是0.75,参考源码:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
当我们自定义CAPACITY时会调用这个构造方法:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
注意这个方法tableSizeFor(initialCapacity);他会将传入的参数进行2的n次幂转换,例如当我们传入7的时候,此方法返回的结果会是8,即我们可能希望初始化容量为7,但实际初始化容量为8。
当调用put方法时,当HashMap中的元素个数(size)超过 threshold = loadFactor * capacity时,就会进行扩容。
if (++size > threshold)
resize();
也就是说,如果我们设置的默认值是7,经过JDK处理之后,HashMap的容量会被设置成8,但是,这个HashMap在元素个数达到 8*0.75 = 6的时候就会进行一次扩容,这明显是我们不希望见到的。
那么,到底设置成什么值比较合理呢?
这里我们可以参考JDK8得HashMap的putAll方法中的实现的,这个实现在guava(21.0版本)也被采用。
这个值的计算方法就是:
return (int) ((float) expectedSize / 0.75F + 1.0F);
比如我们计划向HashMap中放入7个元素的时候,我们通过expectedSize / 0.75F + 1.0F计算,7/0.75 + 1 = 10 ,10经过JDK处理之后,会被设置成16,这就大大的减少了扩容的几率。