【Java 19】使用期望容量创建 HashMap 和 HashSet 的工厂方法
HashMap
和 HashSet
早在 JDK 1.2 就被加入到了 JDK 中。但是开发人员并没有采用最优的方式,来使用这两个类及其子类。
HashMap
、HashSet
、以及子类 LinkedHashMap
、LinkedHashSet
和 WeakHashMap
提供了构造方法来设置初始的 capacity
的值。在实际的使用中,所提供的大小通常是 Map
或 Set
所期望的 capacity
, 而不是初始的 capacity
。
HashMap
除了 capacity
之外,还有一个属性是 load factor
,也就是装填因子。当哈希表中的条目数量超过 capacity
和 load factor
的乘积时,内部的哈希表会被扩容,并重新进行哈希操作。
装填因子的默认值是 0.75
。比如,通过 new HashMap(100)
创建了一个初始容量是 100
的 HashMap
。这里的 100
其实是预期的容量。但是当 HashMap
中的条目数量超过 75
之后,HashMap
会进行一次扩容,对性能造成影响。
为了解决这个问题,Java 19 在 HashMap
、HashSet
及其子类中增加了静态的工厂方法,允许提供期望的容量。HashMap
中对应的方法是 newHashMap
,HashSet
中对应的方法是 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())));
}