【Java 19】使用期望容量创建 HashMap 和 HashSet 的工厂方法

HashMapHashSet 早在 JDK 1.2 就被加入到了 JDK 中。但是开发人员并没有采用最优的方式,来使用这两个类及其子类。

HashMapHashSet、以及子类 LinkedHashMapLinkedHashSetWeakHashMap 提供了构造方法来设置初始的 capacity 的值。在实际的使用中,所提供的大小通常是 MapSet 所期望的 capacity, 而不是初始的 capacity

HashMap 除了 capacity 之外,还有一个属性是 load factor,也就是装填因子。当哈希表中的条目数量超过 capacityload factor 的乘积时,内部的哈希表会被扩容,并重新进行哈希操作。

装填因子的默认值是 0.75。比如,通过 new HashMap(100) 创建了一个初始容量是 100HashMap。这里的 100 其实是预期的容量。但是当 HashMap 中的条目数量超过 75 之后,HashMap 会进行一次扩容,对性能造成影响。

为了解决这个问题,Java 19 在 HashMapHashSet 及其子类中增加了静态的工厂方法,允许提供期望的容量。HashMap 中对应的方法是 newHashMapHashSet 中对应的方法是 newHashSet

新方法的实现也很简单,只是用期望的容量除以装填因子,计算出初始容量,再调用 HashMap 的构造方法。

static int calculateHashMapCapacity(int numMappings) {
    return (int) Math.ceil(numMappings / (double) DEFAULT_LOAD_FACTOR);
}

public static <K, V> HashMap<K, V> newHashMap(int numMappings) {
    return new HashMap<>(calculateHashMapCapacity(numMappings));
}

虽然只是这么一个很小的改动,但是仍然带来了可观的性能提升。

使用的方法:          元素数量:    平均执行时间(纳秒/操作):
构造方法             10           380.079
工厂方法             10           378.929
构造方法             100          4135.819
工厂方法             100          3847.426
构造方法             1000         39970.521
工厂方法             1000         37568.595 

如果明确地知道 HashMap 中条目的数量,使用 Java 19 新增的工厂方法是更好的选择。一个典型的场景是把 List 转换成 Map。在下面的代码中,使用 Collectors.toMap() 方法把 List<User> 转换成 Map<String, User>。使用 HashMap.newHashMap 来提供 HashMap 对象。

public Map<String, User> collect(List<User> users) {
  return users.stream().collect(Collectors.toMap(User::id,
      Function.identity(), (str1, str2) -> str1, () -> HashMap.newHashMap(
      users.size())));
}
版权所有 © 2024 灵动代码