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 的装箱/拆箱开销,内存更紧凑。

可视化图表

IntegerListSum jdk11

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 的优势略有收窄,但依然明显。
  • ✅ 并行效果更明显:相比求和,并行化在过滤这类操作中收益更大,因为每个元素的处理时间更长。

可视化图表

IntegerListFilter jdk11

5. 总结

通过两个典型场景的基准测试,我们可以得出以下结论:

  • 原生类型集合(如 IntList)性能优势巨大:避免装箱拆箱、内存连续,尤其在数值计算场景下,性能可达 JDK 集合的数倍甚至十倍。
  • 并行处理能力更强:Eclipse Collections 提供了更高效的并行实现(asParallel / primitiveParallelStream),在多核环境下能更好利用硬件资源。
  • ⚠️ 适用场景:如果你的应用涉及大量数值计算、大数据量集合操作(如金融、统计、大数据处理),强烈建议评估 Eclipse Collections。
  • 不适用场景:普通 CRUD 业务系统,集合操作不密集,使用 JDK 集合完全足够,引入新依赖反而增加复杂度。

项目源码已托管至 GitHub:https://github.com/example/collection-benchmark(原链接为示例,已脱敏替换)

简单粗暴地说:当你在做“算数”而不是“存数据”时,Eclipse Collections 值得你认真考虑


原始标题:Benchmark JDK Collections vs Eclipse Collections