1. 概述
在本教程中,我们将 对几种流行的 Java 原生类型集合库进行性能对比测试。
具体来说,我们会分别测试这些库中 add()
、get()
和 contains()
方法的执行效率。
2. 性能对比分析
接下来,我们来看看哪个库提供了更高效的原生集合 API。
为此,我们选取了 Trove、Fastutil 和 Colt 这三个库中的 List
类型,并使用 JMH(Java Microbenchmark Harness) 工具来进行基准测试。
2.1. JMH 参数设置
我们的基准测试将使用如下配置参数:
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Measurement(batchSize = 100000, iterations = 10)
@Warmup(batchSize = 100000, iterations = 10)
@State(Scope.Thread)
public class PrimitivesListPerformance {
}
说明如下:
- 使用
@BenchmarkMode(Mode.SingleShotTime)
来测量每次操作的时间。 - 结果以毫秒为单位输出。
- 测试和预热阶段都运行 10 次,每次批量处理 100,000 个元素。
@State(Scope.Thread)
表示每个线程拥有独立的状态实例。
此外,我们定义并初始化各个集合对象:
public static class PrimitivesListPerformance {
private List<Integer> arrayList = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
private TIntArrayList tList = new TIntArrayList(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
private cern.colt.list.IntArrayList coltList = new cern.colt.list.IntArrayList(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
private IntArrayList fastUtilList = new IntArrayList(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
private int getValue = 4;
}
现在可以开始编写基准测试方法了。
3. add()
方法性能
首先测试向集合中添加元素的操作。我们也会测试标准的 ArrayList
作为参照。
3.1. 基准测试代码
标准 ArrayList
的添加操作:
@Benchmark
public boolean addArrayList() {
return arrayList.add(getValue);
}
Trove 的 TIntArrayList.add()
:
@Benchmark
public boolean addTroveIntList() {
return tList.add(getValue);
}
Colt 的 IntArrayList.add()
:
@Benchmark
public void addColtIntList() {
coltList.add(getValue);
}
Fastutil 的 IntArrayList.add()
:
@Benchmark
public boolean addFastUtilIntList() {
return fastUtilList.add(getValue);
}
3.2. 测试结果
运行测试后得到以下结果:
Benchmark Mode Cnt Score Error Units
addArrayList ss 10 4.527 ± 4.866 ms/op
addColtIntList ss 10 1.823 ± 4.360 ms/op
addFastUtilIntList ss 10 2.097 ± 2.329 ms/op
addTroveIntList ss 10 3.069 ± 4.026 ms/op
从结果可以看出:
✅ ArrayList.add()
是最慢的,因为它涉及装箱/拆箱操作。
⚠️ 虽然三款原生集合库底层都使用 int[]
存储数据,但 Colt 在 add()
操作上表现最好。
原因在于:
- Colt 只在数组满时才扩容;
- Trove 和 Fastutil 扩容时会做额外计算,因此稍慢一些。
4. get()
方法性能
接下来测试获取元素的性能。
4.1. 基准测试代码
ArrayList.get()
:
@Benchmark
public int getArrayList() {
return arrayList.get(getValue);
}
Trove 的 TIntArrayList.get()
:
@Benchmark
public int getTroveIntList() {
return tList.get(getValue);
}
Colt 的 IntArrayList.get()
:
@Benchmark
public int getColtIntList() {
return coltList.get(getValue);
}
Fastutil 的 IntArrayList.getInt()
:
@Benchmark
public int getFastUtilIntList() {
return fastUtilList.getInt(getValue);
}
4.2. 测试结果
Benchmark Mode Cnt Score Error Units
getArrayList ss 20 5.539 ± 0.552 ms/op
getColtIntList ss 20 4.598 ± 0.825 ms/op
getFastUtilIntList ss 20 4.585 ± 0.489 ms/op
getTroveIntList ss 20 4.715 ± 0.751 ms/op
✅ ArrayList.get()
略慢于其他原生集合库。
✅ 三款原生集合库的实现几乎一致,都是直接从内部数组中取值,所以性能接近。
5. contains()
方法性能
最后测试查找元素是否存在。
5.1. 基准测试代码
ArrayList.contains()
:
@Benchmark
public boolean containsArrayList() {
return arrayList.contains(getValue);
}
Trove 的 TIntArrayList.contains()
:
@Benchmark
public boolean containsTroveIntList() {
return tList.contains(getValue);
}
Colt 的 IntArrayList.contains()
:
@Benchmark
public boolean containsColtIntList() {
return coltList.contains(getValue);
}
Fastutil 的 IntArrayList.contains()
:
@Benchmark
public boolean containsFastUtilIntList() {
return fastUtilList.contains(getValue);
}
5.2. 测试结果
Benchmark Mode Cnt Score Error Units
containsArrayList ss 20 2.083 ± 1.585 ms/op
containsColtIntList ss 20 1.623 ± 0.960 ms/op
containsFastUtilIntList ss 20 1.406 ± 0.400 ms/op
containsTroveIntList ss 20 1.512 ± 0.307 ms/op
✅ ArrayList.contains()
依旧是最慢的,因为需要频繁装箱/拆箱。
✅ 其他三个库的表现非常接近,选择哪个主要看项目需求和生态支持。
6. 小结
在这篇文章中,我们通过 JMH 对多个 Java 原生集合库进行了基准测试,比较了它们与标准 ArrayList
在 add()
、get()
和 contains()
操作上的性能差异。
📌 请注意:这里展示的只是基准测试的结果,在实际项目中请务必结合自己的业务场景进行实测。
如需查看完整代码,请访问:GitHub - core-java-collections-list-3