这篇文章介绍几个 Java 开发中可以进行性能优化的小技巧,虽然大多数情况下极致优化代码是没有必要的,但是作为一名技术开发者,我们还是想追求代码的更小、更快,更强。如果哪天你发现程序的运行速度不尽人意,可能会想到这篇文章。
提示:我们不应该为了优化而优化,这有时会增加代码的复杂度。
这篇文章中的代码都在以下环境中进行性能测试。
-
JMH version: 1.33(Java 基准测试框架)
-
VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
通过这篇文章的测试,将发现以下几个操作的性能差异。
-
预先分配 HashMap 的大小,提高 1/4 的性能。
-
优化 HashMap 的 key,性能相差 9.5 倍。
-
不使用 Enum.values() 遍历,Spring 也曾如此优化。
-
使用 Enum 代替 String 常量,性能高出 1.5 倍。
-
使用高版本 JDK,基础操作有 2-5 倍性能差异。
预先分配 HashMap 的大小
HashMap 是 Java 中最为常用的集合之一,大多数的操作速度都非常快,但是 HashMap 在调整自身的容量大小时是很慢且难以自动优化,因此我们在定义一个 HashMap 之前,应该尽可能的给出它的容量大小。给出 size 值时要考虑负载因子,HashMap 默认负载因子是 0.75,也就是要设置的 size 值要除于 0.75。
相关文章:HashMap 源码分析解读
下面使用 JMH 进行基准测试,测试分别向初始容量为 16 和 32 的 HashMap 中插入 14 个元素的效率。
/**
* @author https://www.wdbyte.com
*/
@State(Scope.Benchmark)
@Warmup(iterations = 3,time = 3)
@Measurement(iterations = 5,time = 3)
public class HashMapSize {
@Param({"14"})
int keys;
@Param({"16", "32"})
int size;
@Benchmark
public HashMap<Integer, Integer> getHashMap() {
HashMap<Integer, Integer> map = new HashMap<>(size);
for (int i = 0; i < keys; i++) {
map.put(i, i);
}
return map;
}
}
HashMap 的初始容量是 16,负责因子 0.75,即最多插入 12 个元素,再插入时就要进行扩容,所以插入 14 个元素过程中需要扩容一次,但是如果 HashMap 初始化时就给了 32 容量,那么最多可以承载 32 * 0.75 = 24
个元素,所以插入 14 个元素时是不需要扩容操作的。
# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
Benchmark (keys) (size) Mode Cnt Score Error Units
HashMapSize.getHashMap 14 16 thrpt 25 4825825.152 ± 323910.557 ops/s
HashMapSize.getHashMap 14 32 thrpt 25 6556184.664 ± 711657.679 ops/s
可以看到在这次测试中,初始容量为32 的 HashMap 比初始容量为 16 的 HashMap 每秒可以多操作 26% 次,已经有 1/4 的性能差异了。
优化 HashMap 的 key
如果 HashMap 的 key 值需要用到多个 S