原文地址:xeblog.cn/articles/26测试
注:本文全部代码示例均基于
JDK8
。this
经过查看 HashMap
的源码能够得知其默认的初始容量为 16
,默认的加载因子为 0.75
。spa
/** * The default initial capacity - MUST be a power of two. * 默认的初始容量(必须是2的N次幂),默认为2^4=16 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/** * The load factor used when none specified in constructor. * 默认的加载因子为0.75 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
复制代码
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */
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);
}
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
复制代码
通常状况下都是经过这三种构造方法来初始化 HashMap
的。经过默认的无参构造方法初始化后 HashMap
的容量就是默认的16,加载因子也是默认的0.75;经过带参数的构造方法能够初始化一个自定义容量和加载因子的 HashMap
。一般状况下,加载因子使用默认的0.75就好。code
HashMap
的容量要求必须是2的N次幂,这样能够提升散列的均匀性,下降 Hash
冲突的风险。可是容量能够经过构造方法传入的,若是我传入一个非2次幂的数进去呢?好比3?传进去也不会干吗呀,又不报错。。。哈哈哈哈。 是的,不会报错的,那是由于 HashMap
本身将这个数转成了一个最接近它的2次幂的数。这个转换的方法是 tableSizeFor(int cap)
。cdn
/** * Returns a power of two size for the given target capacity. */
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
复制代码
这个方法会将传入的数转换成一个2次幂的数,好比传入的是3,则返回的是4;传入12,则返回的是16。blog
加载因子和 HashMap
的扩容机制有着很是重要的联系,它能够决定在何时才进行扩容。HashMap
是经过一个阀值来肯定是否扩容,当容量超过这个阀值的时候就会进行扩容,而加载因子正是参与计算阀值的一个重要属性,阀值的计算公式是 容量 * 加载因子
。若是经过默认构造方法建立 HashMap
,则阀值为 16 * 0.75 = 12
,就是说当 HashMap
的容量超过12的时候就会进行扩容。内存
/** * The next size value at which to resize (capacity * load factor). * * @serial */
int threshold;
复制代码
这是 HashMap
的 putVal(...)
方法的一个片断,put(...)
方法其实就是调用的这个方法,size
是当前 HashMap
的元素个数,当元素个数+1后超过了阀值就会调用 resize()
方法进行扩容。ci
if (++size > threshold)
resize();
复制代码
加载因子在通常状况下都最好不要去更改它,默认的0.75是一个很是科学的值,它是通过大量实践得出来的一个经验值。当加载因子设置的比较小的时候,阀值就会相应的变小,扩容次数就会变多,这就会致使 HashMap
的容量使用不充分,还没添加几个值进去就开始进行了扩容,浪费内存,扩容效率还很低;当加载因子设置的又比较大的时候呢,结果又很相反,阀值变大了,扩容次数少了,容量使用率又提升了,看上去是很不错,实际上仍是有问题,由于容量使用的越多,Hash
冲突的几率就会越大。因此,选择一个合适的加载因子是很是重要的。get
经过默认构造方法建立一个 HashMap
,并循环添加13个值源码
当添加第1个值后,容量为16,加载因子为0.75,阀值为12
当添加完第13个值后,执行了扩容操做,容量变为了32,加载因子不变,阀值变为了24
建立一个初始容量为12(非2次幂数)的 HashMap
,并添加1个值
建立一个初始容量为2的 HashMap
,并添加2个值
当添加完第1个值后,容量为2,加载因子为0.75,阀值为1
当添加完第2个值后,执行了扩容操做,容量变为4,加载因子为0.75,阀值为3
建立一个初始容量为二、加载因子为1的 HashMap
,并添加2个值
当添加完第1个值后,容量为2,加载因子为1,阀值为2
当添加完第2个值后,并无执行扩容操做,容量、加载因子、阀值均没有变化