您应该将初始容量视为对HashMap 的大致预期数据的提示。通过提供正确的初始容量,您可以最大程度地减少必须重新构建地图以进行扩展的次数。例如,如果您知道您计划插入一百万条记录,则通过构建具有 1,000,000 初始容量的映射,它将确保在构建时分配足够的内存来处理这么多的插入。在那之后,在map.put() 调用期间,将来插入地图可能需要大的 O(n) 操作才能调整大小。
将此初始容量视为提示,而不是您期望 HashMap 遵循的指令,可能会帮助您了解您所描述的优化是不必要的。 HashMap 旨在在所有正常情况下都表现良好,因此虽然提供初始容量可能会有所帮助,但它通常不会对您的代码产生巨大影响,除非您正在构建 许多 新的大型地图每时每刻。在这种情况下,指定容量可以避免中间表调整大小,仅此而已。
As documented,如果您指定的初始容量太大,您可能会引入一些不必要的减速:
对集合视图的迭代需要的时间与HashMap 实例的“容量”成正比
但实际上,分配如此大的映射所浪费的内存可能会比迭代速度稍慢的迭代速度更快地给您带来问题。
请务必阅读Why does HashMap require that the initial capacity be a power of two?。
您可能会考虑的一件事是切换到Guava 的ImmutableMap 实现;如果您事先知道地图的内容,并且您不希望更改它们,那么不可变集合将被证明更易于使用和use less memory than their mutable counterparts。
以下是我使用 Scala 的 REPL(和一些个人实用功能)进行的一些快速检查,以检查 HashMap (Java 1.7) 内部发生的情况:
// Initialize with capacity=7
scala> new HashMap[String,String](7)
res0: java.util.HashMap[String,String] = {}
scala> getPrivate(res0, "table").length
res1: Int = 8
scala> ... put 7 values
// Still internally using the same array
scala> getPrivate(res0, "table").length
res9: Int = 8
// Specifying capacity 9 allocates a 16-lenth array
scala> getPrivate(new HashMap[String,String](9), "table").length
res10: Int = 16
// Copying our first map into a new map interestingly
// also allocates the default 16 slots, rather than 8
scala> getPrivate(new HashMap[String,String](res0), "table").length
res11: Int = 16
scala> ... put 10 more values in our map
scala> getPrivate(res0,"table").length
res22: Int = 32
// Copying again immediately jumps to 32 capacity
scala> getPrivate(new HashMap[String,String](res0),"table").length
res23: Int = 32