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 仓库


原始标题:Guide to FastUtil