HashMap是我们开发中常用的一个容器。但是通常情况下,创建HashMap所采用的策略莫过于一句“new HashMap()”。然而,Java本身提供了除空参以外的其他两种方法。分别是:
HashMap(int initialCapacity)
构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。
HashMap(int initialCapacity, float loadFactor)
构造一个带指定初始容量和加载因子的空 HashMap。
HashMap(Map<? extends K,? extends V> m)
构造一个映射关系与指定 Map 相同的新 HashMap。
这三个方法中,除第三种以外,都说明了构造一个指定初始容量的参数---initialCapacity。HashMap默认创建的初始容量是16,而此处的参数指定了初始容量的大小。但最终创建的容量是否为我们所指定的大小?做个例子。首先是默认构造:
Map<String, Object> hashMap = new HashMap<String, Object>();
Field field = HashMap.class.getDeclaredField("DEFAULT_INITIAL_CAPACITY");
field.setAccessible(true);
System.out.println(field.get(hashMap));
查看控制台输出可以看见输出值为16。当然,直接查看源代码也能看到这个初始变量的定义。那么我们再换个方案,指定初始容量为9试试。
Map<String, Object> hashMap = new HashMap<String, Object>(9);
Field field = HashMap.class.getDeclaredField("threshold");
field.setAccessible(true);
int threshold = (Integer)field.get(hashMap);
System.out.println(threshold/0.75);
这里再看,发现输出值为16而非9。此外,为何这次获取容量会使用threshold/0.75的方案?这里需要解释下。threshold的值是HashMap在初始化过程中赋予的,目的是为了当HashMap中键值对数量达到一定标准时进行拓容。而此处的threshold就是这个标准。它的计算方案为:threshold=当前容量*当前加载因子。加载因子因为没有指定,默认即为0.75,因此,当前容量即为threshold/当前加载因子(0.75)。得出答案为16。
为什么我们指定了初始容量为9,而构造出来的结果却是16呢?跟入HashMap的初始化代码处看到如下内容:
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
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);
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
解读之后发现。我们默认设定的初始容量其实并不是实际容量大小,实际容量大小是由capacity决定的。而这个变量的值初始为1,通过
while (capacity < initialCapacity)
capacity <<= 1;
这个方法来计算。此时就变成了
capacity | initialCapacity | capacity < initialCapacity |
1 | 9 | true |
2 | 9 | true |
4 | 9 | true |
8 | 9 | true |
16 | 9 | false |
于是乎,最后初始化所得到的容量为16。原因就是因为capacity这个变量的增长运算采用的左移运算。而非直接采用capacity=initialCapacity的方案。