如何给HashMap设置初始化容量
在脉脉上看到一个帖子,大致是说,leader的代码在创建HashMap对象时,会主动设置初始容量大小,不知道这么操作到底对不对。
是否需要设置初始容量
答案是:如有必要,尽量设置。
为什么?因为随着元素的增加,Map会进行多次resize(扩容),影响性能。假如我们已经知道要添加的元素数量,创建Map对象就将容量设置到位,就可避免resize操作。
这也是阿里的Java开发手册中,明确推荐“集合初始化时,指定集合初始值大小”。并且推荐设置初始容量为initialCapacity=(需要存储的元素个数 / 负载因子) + 1。
如何设置初始容量
根据阿里开发手册的推荐,初始容量设置为initialCapacity=(需要存储的元素个数 / 负载因子) + 1,为何要如此设置呢?
如帖子中所说
比如给mybatis传5个参,就new HashMap<>(5)
在创建对象时,会将实际的容量值,设置为比该容量值(5)大且最接近的2的幂(也就是8)。
在添加元素时,当元素个数大于容量阈值(容量阈值 = 容量值 * 负载因子)时,HashMap会进行扩容。
此处添加的元素个数 5 < 8 * 0.75 ,因此不会进行扩容。
但假若要添加7个元素,初始容量参数也设置为7:new HashMap<>(7)
此时HashMap对象的初始容量仍会被设置为 8 ,但是当添加到第 6 个元素时,就会触发一次扩容。
因此,需要添加多少个元素,就将HashMap的容量设置为多少,不太合适。
设置初始容量大小为initialCapacity=(需要存储的元素个数 / 负载因子) + 1,就是为了尽可能减少扩容的几率。如上需要添加 7 个元素时,initialCapacity = (7 / 0.75) + 1 = 10。
再经HashMap初始化处理后,实际容量被设置为 16,添加 7 个元素就不会发生resize操作。
推荐使用 guava 提供的集合类方法 Maps.newHashMapWithExpectedSize(expectedSize),该方法对 HashMap 的初始容量计算就是这个实现。
public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(int expectedSize) { return new HashMap(capacity(expectedSize)); } static int capacity(int expectedSize) { if (expectedSize < 3) { CollectPreconditions.checkNonnegative(expectedSize, "expectedSize"); return expectedSize + 1; } else { return expectedSize < 1073741824 ? (int)((float)expectedSize / 0.75F + 1.0F) : 2147483647; } }
假如你不关心 HashMap 扩容带来的性能影响,又不想被 Java 编码规约频繁提醒,也可使用 guava 提供的 Maps.newHashMap() 方法创建 HashMap 对象。