1. 简介
本文将对比传统 JDK 集合与 Eclipse Collections 的性能表现。我们设计多个典型场景进行基准测试,并分析结果,帮助你在实际项目中做出更合理的技术选型。
这类测试不是纸上谈兵,而是真实踩坑后的总结。尤其在高并发、大数据量场景下,集合操作的性能差异可能直接影响系统吞吐量。
2. 测试环境配置
本次测试采用默认 JVM 配置,未添加任何调优参数,确保测试环境贴近常规生产设置。
硬件与软件环境
- JVM: JDK 11.0.3,Java HotSpot(TM) 64-Bit Server VM
- 硬件: MacPro,2.6GHz 6核 i7,16GB DDR4 内存
- Eclipse Collections: 10.0.0 版本(撰写时最新稳定版)
- 基准测试框架: JMH (Java Microbenchmark Harness)
- 结果可视化: JMH Visualizer(http://jmh.morethan.io/)
项目初始化
使用 Maven 快速搭建 JMH 测试项目:
mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeGroupId=org.openjdk.jmh \
-DarchetypeArtifactId=jmh-java-benchmark-archetype \
-DgroupId=com.example.benchmark \
-DartifactId=collection-benchmark \
-Dversion=1.0
随后在 pom.xml
中添加 Eclipse Collections 依赖:
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>10.0.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections-api</artifactId>
<version>10.0.0</version>
</dependency>
✅ 建议:使用 eclipse-collections
主包即可,API 包通常由主包传递依赖。
3. 第一个基准测试:整数求和
测试目标:对包含 100 万个整数的列表进行求和操作,对比不同集合实现的吞吐量(ops/s,越高越好)。
测试用例设计
我们对比六种实现方式:
- JDK 普通 List(串行/并行)
- Eclipse Collections 可变 List(串行/并行)
- Eclipse Collections 原生 int 类型 List(串行/并行)
示例代码
private List<Integer> jdkIntList;
private MutableList<Integer> ecMutableList;
private ExecutorService executor;
private IntList ecIntList;
@Setup
public void setup() {
PrimitiveIterator.OfInt iterator = new Random(1L).ints(-10000, 10000).iterator();
ecMutableList = FastList.newWithNValues(1_000_000, iterator::nextInt);
jdkIntList = new ArrayList<>(1_000_000);
jdkIntList.addAll(ecMutableList);
ecIntList = ecMutableList.collectInt(i -> i, new IntArrayList(1_000_000));
executor = Executors.newWorkStealingPool();
}
@Benchmark
public long jdkList() {
return jdkIntList.stream().mapToLong(i -> i).sum();
}
@Benchmark
public long ecMutableList() {
return ecMutableList.sumOfInt(i -> i);
}
@Benchmark
public long jdkListParallel() {
return jdkIntList.parallelStream().mapToLong(i -> i).sum();
}
@Benchmark
public long ecMutableListParallel() {
return ecMutableList.asParallel(executor, 100_000).sumOfInt(i -> i);
}
@Benchmark
public long ecPrimitive() {
return this.ecIntList.sum();
}
@Benchmark
public long ecPrimitiveParallel() {
return this.ecIntList.primitiveParallelStream().sum();
}
执行命令
mvn clean install
java -jar target/benchmarks.jar IntegerListSum -rf json
测试结果(吞吐量)
基准方法 | 吞吐量 (ops/s) | 误差 |
---|---|---|
ecMutableList |
573.016 | ±35.865 |
ecMutableListParallel |
1251.353 | ±705.196 |
✅ ecPrimitive |
4067.901 | ±258.574 |
✅ ecPrimitiveParallel |
8827.092 | ±11143.823 |
jdkList |
568.696 | ±7.951 |
jdkListParallel |
918.512 | ±27.487 |
关键结论
- ❌ JDK 并行流性能提升有限,仅比串行高约 60%。
- ✅ Eclipse Collections 原生
IntList
表现惊人,串行性能是 JDK 的 7 倍以上。 - ✅ 并行原生
IntList
吞吐量接近 9000 ops/s,是 JDK 并行流的 近 10 倍。 - ⚠️ 性能优势主要来自:避免了 Integer 的装箱/拆箱开销,内存更紧凑。
可视化图表
4. 过滤操作性能测试
测试目标:筛选出所有能被 5 整除的元素,观察过滤场景下的性能差异。
示例代码
private List<Integer> jdkIntList;
private MutableList<Integer> ecMutableList;
private IntList ecIntList;
private ExecutorService executor;
@Setup
public void setup() {
PrimitiveIterator.OfInt iterator = new Random(1L).ints(-10000, 10000).iterator();
ecMutableList = FastList.newWithNValues(1_000_000, iterator::nextInt);
jdkIntList = new ArrayList<>(1_000_000);
jdkIntList.addAll(ecMutableList);
ecIntList = ecMutableList.collectInt(i -> i, new IntArrayList(1_000_000));
executor = Executors.newWorkStealingPool();
}
@Benchmark
public List<Integer> jdkList() {
return jdkIntList.stream().filter(i -> i % 5 == 0).collect(Collectors.toList());
}
@Benchmark
public MutableList<Integer> ecMutableList() {
return ecMutableList.select(i -> i % 5 == 0);
}
@Benchmark
public List<Integer> jdkListParallel() {
return jdkIntList.parallelStream().filter(i -> i % 5 == 0).collect(Collectors.toList());
}
@Benchmark
public MutableList<Integer> ecMutableListParallel() {
return ecMutableList.asParallel(executor, 100_000).select(i -> i % 5 == 0).toList();
}
@Benchmark
public IntList ecPrimitive() {
return this.ecIntList.select(i -> i % 5 == 0);
}
@Benchmark
public IntList ecPrimitiveParallel() {
return this.ecIntList.primitiveParallelStream()
.filter(i -> i % 5 == 0)
.collect(IntLists.mutable::empty, MutableIntList::add, MutableIntList::addAll);
}
执行命令
mvn clean install
java -jar target/benchmarks.jar IntegerListFilter -rf json
测试结果(吞吐量)
基准方法 | 吞吐量 (ops/s) | 误差 |
---|---|---|
ecMutableList |
145.733 | ±7.000 |
ecMutableListParallel |
603.191 | ±24.799 |
✅ ecPrimitive |
232.873 | ±8.032 |
✅ ecPrimitiveParallel |
1029.481 | ±50.570 |
jdkList |
155.284 | ±4.562 |
jdkListParallel |
445.737 | ±23.685 |
关键结论
- ✅ 原生
IntList
依然领先,串行性能优于 JDK。 - ✅ 并行过滤性能提升显著,Eclipse Collections 原生并行实现比 JDK 高出 约 2.3 倍。
- ⚠️ 注意:过滤操作本身计算成本高于求和,因此装箱/拆箱的相对开销占比下降,Eclipse Collections 的优势略有收窄,但依然明显。
- ✅ 并行效果更明显:相比求和,并行化在过滤这类操作中收益更大,因为每个元素的处理时间更长。
可视化图表
5. 总结
通过两个典型场景的基准测试,我们可以得出以下结论:
- ✅ 原生类型集合(如
IntList
)性能优势巨大:避免装箱拆箱、内存连续,尤其在数值计算场景下,性能可达 JDK 集合的数倍甚至十倍。 - ✅ 并行处理能力更强:Eclipse Collections 提供了更高效的并行实现(
asParallel
/primitiveParallelStream
),在多核环境下能更好利用硬件资源。 - ⚠️ 适用场景:如果你的应用涉及大量数值计算、大数据量集合操作(如金融、统计、大数据处理),强烈建议评估 Eclipse Collections。
- ❌ 不适用场景:普通 CRUD 业务系统,集合操作不密集,使用 JDK 集合完全足够,引入新依赖反而增加复杂度。
项目源码已托管至 GitHub:https://github.com/example/collection-benchmark(原链接为示例,已脱敏替换)
简单粗暴地说:当你在做“算数”而不是“存数据”时,Eclipse Collections 值得你认真考虑。