1. 简介
FastUtil 是一个用于增强 Java 集合框架的库。它提供了一系列 类型特定的集合类(type-specific collections),例如 Map、Set、List 和 Queue,相比标准的 Java 集合,它们在内存占用和性能访问上更优。
我们将在本文中:
✅ 介绍 FastUtil 的核心功能
✅ 通过 JMH 对比 FastUtil 与 Java 原生集合的性能
✅ 使用 BigArray 工具处理超大数组
FastUtil 的设计目标是极致性能,这也是它得名的原因。
2. FastUtil 的主要特性
FastUtil 扩展了 Java 集合框架,提供了类型特定的集合实现,包括:
- 高性能的 Map、Set、List、Queue
- 支持 64 位的大数组(BigArray)操作
- 丰富的 IO 工具类,支持二进制和文本文件处理
- 自 Java 8 起支持函数式接口(Function Interface)
FastUtil 的集合类通过避免泛型包装和装箱拆箱操作,显著提升了性能和内存效率。
2.1. 性能优势
FastUtil 官方提供了一份详尽的 性能对比报告,与 Trove、HPPC 等库对比,FastUtil 多数情况下性能最优。
我们也会使用 JMH 来验证这一点。
3. 依赖配置
FastUtil 需要引入以下 Maven 依赖:
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil</artifactId>
<version>8.2.2</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
<scope>test</scope>
</dependency>
Gradle 用户可使用:
testImplementation 'org.openjdk.jmh:jmh-core:1.37'
testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.37'
implementation 'it.unimi.dsi:fastutil:8.2.2'
3.1. 定制 Jar 包
FastUtil 由于为每种基本类型都生成了独立的类,所以默认 jar 文件非常大。可以通过其提供的 find-deps.sh
脚本,生成只包含所需类的小型 jar 包,便于生产环境使用。
4. 类型特定集合
FastUtil 提供了类型特定的集合类,避免了泛型和装箱带来的性能损耗。
举个例子,使用 Double2DoubleMap
来存储 double 类型的键值对:
Double2DoubleMap d2dMap = new Double2DoubleOpenHashMap();
d2dMap.put(2.0, 5.5);
d2dMap.put(3.0, 6.6);
assertEquals(5.5, d2dMap.get(2.0));
这个接口和实现类与 Java 集合接口非常相似,但性能更高,内存更省。
4.1. 性能对比测试
我们使用 JMH 来对比 FastUtil 的 IntOpenHashSet
和 Java 原生的 HashSet<Integer>
的性能。
FastUtil 实现:
@Param({"100", "1000", "10000", "100000"})
public int setSize;
@Benchmark
public IntSet givenFastUtilsIntSetWithInitialSizeSet_whenPopulated_checkTimeTaken() {
IntSet intSet = new IntOpenHashSet(setSize);
for(int i = 0; i < setSize; i++) {
intSet.add(i);
}
return intSet;
}
Java Collections 实现:
@Benchmark
public Set<Integer> givenCollectionsHashSetWithInitialSizeSet_whenPopulated_checkTimeTaken() {
Set<Integer> intSet = new HashSet<>(setSize);
for(int i = 0; i < setSize; i++) {
intSet.add(i);
}
return intSet;
}
测试结果如下:
Benchmark (setSize) Mode Cnt Score Units
givenCollectionsHashSetWithInitialSizeSet... 100 avgt 2 1.460 us/op
givenCollectionsHashSetWithInitialSizeSet... 1000 avgt 2 12.740 us/op
givenCollectionsHashSetWithInitialSizeSet... 10000 avgt 2 109.803 us/op
givenCollectionsHashSetWithInitialSizeSet... 100000 avgt 2 1870.696 us/op
givenFastUtilsIntSetWithInitialSizeSet... 100 avgt 2 0.369 us/op
givenFastUtilsIntSetWithInitialSizeSet... 1000 avgt 2 2.351 us/op
givenFastUtilsIntSetWithInitialSizeSet... 10000 avgt 2 37.789 us/op
givenFastUtilsIntSetWithInitialSizeSet... 100000 avgt 2 896.467 us/op
✅ FastUtil 实现在性能上明显优于 Java 原生集合,尤其是在大数据量时表现更为突出。
5. 大数组(BigArray)支持
FastUtil 还支持处理超过 2^31 - 1 元素的数组(即 64 位数组),这是 Java 原生数组无法做到的。
使用 IntBigArrays
来处理大数组示例:
int[] oneDArray = new int[] { 2, 1, 5, 2, 1, 7 };
int[][] twoDArray = IntBigArrays.wrap(oneDArray.clone());
int firstIndex = IntBigArrays.get(twoDArray, 0);
int lastIndex = IntBigArrays.get(twoDArray, IntBigArrays.length(twoDArray)-1);
assertEquals(2, firstIndex);
assertEquals(7, lastIndex);
⚠️ 注意:使用 clone()
是为了确保进行深拷贝,避免原始数组被修改影响结果。
6. 总结
FastUtil 是一个专注于性能和内存优化的 Java 集合库,适合处理大量数据或对性能要求较高的场景。
本文我们重点介绍了:
- FastUtil 的类型特定集合类
- 使用 JMH 对比了其与 Java 原生集合的性能
- 使用 BigArray 工具处理超大数组
如果你正在寻找一个高性能的集合库,FastUtil 是一个非常值得考虑的选择。
完整代码示例请参考 GitHub 仓库。