固定大小的HashMap的最佳容量和负载因子是多少?
固定大小的HashMap的最佳容量和负载因子是多少?
我正在尝试找出一个特定情况下的最佳容量和负载因子。我认为我已经大致了解了,但我仍然感谢比我更有经验的人确认一下。:)
如果我知道我的HashMap将填充到包含100个对象的容量,并且大部分时间都会有100个对象,我猜最佳值是初始容量100和负载因子1?还是我需要容量101,或者还有其他需要注意的地方吗?
编辑:好的,我花了几个小时进行了一些测试。以下是结果:
- 奇怪的是,容量、容量+1、容量+2、容量-1,甚至容量-10都产生完全相同的结果。我原本期望至少容量-1和容量-10会产生更差的结果。
- 使用初始容量(而不是使用默认值16)会显著提高put()的性能-快了30%。
- 使用负载因子1对少量对象的性能没有影响,对较大数量的对象(>100000)有更好的性能。然而,这并不与对象数量成比例改善;我怀疑还有其他影响结果的因素。
- get()的性能在不同数量的对象/容量下略有不同,但尽管在每种情况下可能略有变化,但通常不受初始容量或负载因子的影响。
编辑2:我也添加了一些图表。这是一个显示负载因子0.75和1之间差异的图表,其中我初始化HashMap并将其填满到满容量。y轴是以毫秒为单位的时间(较低为较好),x轴是大小(对象数量)。由于大小线性变化,所需时间也线性增长。
所以,让我们看看我得到了什么。以下两个图表显示了负载因子之间的差异。第一个图表显示了当HashMap填满容量时会发生什么;负载因子0.75由于重新调整大小而性能较差。然而,并不一直较差,并且有各种各样的颠簸和跳跃-我猜垃圾回收在其中起了很大的作用。负载因子1.25与1相同,因此未包含在图表中。
此图表证明了0.75由于重新调整大小而性能较差;如果我们将HashMap填充到半容量,0.75并不较差,只不过是...不同(并且应该使用更少的内存并且具有不可察觉地更好的迭代性能)。
还有一件事我想展示。这是所有三个负载因子和不同HashMap大小的get性能。除了负载因子1的一个峰值外,一致地保持不变并略有变化。我真的很想知道那是什么(可能是垃圾回收,但谁知道呢)。
这是代码,供有兴趣的人参考:
import java.util.HashMap; import java.util.Map; public class HashMapTest { // 容量- 高达10000000的数字需要 -mx1536m -ms1536m JVM参数 public static final int CAPACITY = 10000000; public static final int ITERATIONS = 10000; // 设置为false以打印put性能,或设置为true以打印get性能 boolean doIterations = false; private Mapcache; public void fillCache(int capacity) { long t = System.currentTimeMillis(); for (int i = 0; i <= capacity; i++) cache.put(i, "Value number " + i); if (!doIterations) { System.out.print(System.currentTimeMillis() - t); System.out.print("\t"); } } public void iterate(int capacity) { long t = System.currentTimeMillis(); for (int i = 0; i <= ITERATIONS; i++) { long x = Math.round(Math.random() * capacity); String result = cache.get((int) x); } if (doIterations) { System.out.print(System.currentTimeMillis() - t); System.out.print("\t"); } } public void test(float loadFactor, int divider) { for (int i = 10000; i <= CAPACITY; i+= 10000) { cache = new HashMap (i, loadFactor); fillCache(i / divider); if (doIterations) iterate(i / divider); } System.out.println(); } public static void main(String[] args) { HashMapTest test = new HashMapTest(); // 填满容量 test.test(0.75f, 1); test.test(1, 1); test.test(1.25f, 1); // 填满一半容量 test.test(0.75f, 2); test.test(1, 2); test.test(1.25f, 2); } }