固定大小的HashMap的最佳容量和负载因子是多少?

9 浏览
0 Comments

固定大小的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相同,因此未包含在图表中。

fully filled

此图表证明了0.75由于重新调整大小而性能较差;如果我们将HashMap填充到半容量,0.75并不较差,只不过是...不同(并且应该使用更少的内存并且具有不可察觉地更好的迭代性能)。

half full

还有一件事我想展示。这是所有三个负载因子和不同HashMap大小的get性能。除了负载因子1的一个峰值外,一致地保持不变并略有变化。我真的很想知道那是什么(可能是垃圾回收,但谁知道呢)。

go spike

这是代码,供有兴趣的人参考:

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 Map cache;
  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);
  }
}

0