👉这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:
《项目实战(视频)》:从书中学,往事中“练”
《互联网高频面试题》:面朝简历学习,春暖花开
《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题
《精进 Java 学习指南》:系统学习,互联网主流技术栈
《必读 Java 源码专栏》:知其然,知其所以然
👉这是一个或许对你有用的开源项目
国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构
RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRM、AI大模型、IoT物联网等功能:
多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro
微服务:https://gitee.com/zhijiantianya/yudao-cloud
视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本
1. 为什么选择 FastUtil?
2. 核心集合类型速查表(记住这 8 个就够日常 95% 场景)
3. 最佳实践代码示例(直接可复制)
4. Maven/Gradle 依赖(2025 最新)
5. 生产环境避坑清单(血泪经验)
6. 结论:一条替换原则
FastUtil 是由意大利计算机科学家 Sebastiano Vigna 维护的开源库,它为 Java 原始类型(primitive types)提供了类型特化的集合实现,性能通常比 JDK 集合快2~5 倍,内存占用降低40%~70%。在高性能后端、游戏服务器、大数据处理、量化交易等场景中,几乎是标配。
本文总结 2025 年最新的 FastUtil(当前版本 8.5.15+)常用 API 及生产级最佳实践,帮你避开所有常见坑。
1. 为什么选择 FastUtil?
场景 | JDK HashMap<Integer, Long> | FastUtil Int2LongOpenHashMap |
|---|---|---|
内存占用(1000万条) | ~1.1 GB | ~320 MB |
put/get 速度 | 基准 | 2.8~4.5× |
GC 压力 | 高(大量 Integer/Long 包装对象) | 极低(零装箱) |
场景 | JDK HashMap<String, Object> | FastUtil Object2ObjectOpenHashMap<String, Object> |
|---|---|---|
内存占用(1000万条) | ~1.2 GB | ~720 MB |
put/get 速度 | 基准 | 1.5~3×(优化哈希 + 引用相等) |
GC 压力 | 中等(String 不可变,但 Object 可能有引用) | 低(高效迭代 + 零额外包装) |
关键:String 不是原始类型,所以无String2StringOpenHashMap专用类,但Object2ObjectOpenHashMap<String, String>等通用类已足够高效。启用引用相等(reference equality)可进一步加速(用==而非equals())。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
2. 核心集合类型速查表(记住这 8 个就够日常 95% 场景)
原始类型 | List | Set | Map(key→value) |
|---|---|---|---|
| Object (e.g., String) | ObjectArrayList | ObjectOpenHashSet | Object2ObjectOpenHashMap<String, Object>、Object2ObjectOpenHashMap<String, String> |
int | IntArrayList | IntOpenHashSet | Int2IntOpenHashMap、Int2ObjectOpenHashMap |
long | LongArrayList | LongOpenHashSet | Long2LongOpenHashMap、Long2ObjectOpenHashMap |
double | DoubleArrayList | DoubleOpenHashSet | Double2DoubleOpenHashMap |
float | FloatArrayList | FloatOpenHashSet | —— |
byte | ByteArrayList | ByteOpenHashSet | Byte2IntOpenHashMap |
char | CharArrayList | CharOpenHashSet | —— |
short | ShortArrayList | —— | —— |
boolean | —— | BooleanOpenHashSet | —— |
推荐永远使用OpenHash系列(默认实现),它比旧的 RBTree/Champ 更快且内存更省。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
3. 最佳实践代码示例(直接可复制)
3.1 基本替换(最常见)
// 差:大量装箱 + 高内存 Map<Integer, Long> map = new HashMap<>(); // 好:零装箱 + 极致性能 Int2LongOpenHashMap map = new Int2LongOpenHashMap(); // 常用构造方式 Int2LongOpenHashMap map = new Int2LongOpenHashMap(1_000_000); // 预估容量 Int2LongOpenHashMap map = new Int2LongOpenHashMap(1_000_000, 0.8f); // 指定负载因子3.2 推荐初始化方式(避免频繁扩容)
// 最佳:预估容量 + 高负载因子(FastUtil 默认 0.8~0.9,比 JDK 0.75 高) int expectedSize = 5_000_000; Int2ObjectOpenHashMap<User> userMap = new Int2ObjectOpenHashMap<>(expectedSize, 0.9f); // 如果你能接受极少数 rehash,负载因子甚至可以调到 0.95f Int2IntOpenHashMap counter = new Int2IntOpenHashMap(100_000, 0.95f);3.3 高频操作性能对比 & 推荐写法
Int2LongOpenHashMap map = new Int2LongOpenHashMap(); // 1. get 默认值(避免 containsKey + get 两次查找) long value = map.getOrDefault(userId, 0L); // 推荐 long value = map.containsKey(id) ? map.get(id) : 0L; // 慢 2 倍! // 2. 计数器模式(比 compute 快 3~5 倍) map.addTo(userId, 1L); // 原子 + 极快 // 等价于 map.put(userId, map.getOrDefault(userId, 0L) + 1); // 3. 自增 1 的最快写法 map.addTo(key, 1L); // 4. 批量插入(FastUtil 独有 API,比 putAll 快 30%) int[] keys = ...; long[] values = ...; map.putAll(IntArrays.forceCopy(keys), LongArrays.forceCopy(values), keys.length);3.4 List 使用技巧
// 动态数组(比 ArrayList<Integer> 快 3~5 倍) IntArrayList list = new IntArrayList(); list.add(1); list.add(2); // 快速转成原始数组(零拷贝!) int[] array = list.elements(); // 注意:不要再往 list 里 add! int[] safeArray = list.toIntArray(); // 推荐:防御性拷贝 // 从已有数组创建(零拷贝) int[] raw = new int[]{1,2,3,4}; IntArrayList list = IntArrayList.wrap(raw); // 直接包装,不复制3.5 Set 使用技巧
IntOpenHashSet set = new IntOpenHashSet(1_000_000, 0.9f); set.add(123); if (set.add(123)) { /* 第一次插入 */ } // 快速转原始数组 int[] array = set.toArray(new int[set.size()]);3.6 与 Java Stream 配合(推荐方式)
Int2LongOpenHashMap map = ...; // FastUtil 自带原始流,比装箱流快 5~10 倍 long sum = map.int2LongEntrySet() .fastForEach(entry -> total += entry.getLongValue()); // 或者并行原始流 map.int2LongEntrySet().parallelStream() .forEach(entry -> updateSomeGlobalCounter(entry));3.7 序列化注意事项
// FastUtil 默认实现了 Serializable,但建议显式指定版本 private static final long serialVersionUID = 1L; // 大 Map 序列化建议使用 FastUtil 自带的二进制格式(比 JDK 快 5~10 倍) ByteBufferOutput out = ...; Int2LongBinaryOpenHashMap.write(out, map); // 极快!3.8 String-Object / String-String 替换(最常见场景)
// 差:JDK 通用,性能一般 Map<String, Object> map = new HashMap<>(); Map<String, String> config = new HashMap<>(); // 好:FastUtil Object 优化,内存/速度提升明显 Object2ObjectOpenHashMap<String, Object> objMap = new Object2ObjectOpenHashMap<>(); Object2ObjectOpenHashMap<String, String> strMap = new Object2ObjectOpenHashMap<>(); // 常用构造:预估容量 + 负载因子(避免 rehash) int expectedSize = 500_000; // 如配置项或缓存 Object2ObjectOpenHashMap<String, Object> objMap = new Object2ObjectOpenHashMap<>(expectedSize, 0.9f); Object2ObjectOpenHashMap<String, String> strMap = new Object2ObjectOpenHashMap<>(expectedSize, 0.9f); // 启用引用相等(推荐:String 场景下加速 20%~30%,但需确保无 null) objMap.referenceEquality(); // 或 strMap.referenceEquality();3.9 高频操作性能对比 & 推荐写法(Object 版)
Object2ObjectOpenHashMap<String, Object> map = new Object2ObjectOpenHashMap<>(); // 1. get 默认值(单次查找,避免 containsKey + get) Object value = map.getOrDefault("userKey", null); // 推荐,零额外开销 // 2. 合并操作(Object 版 computeIfAbsent,比 JDK 快 2x) map.computeIfAbsent("key", k -> new Object()); // 如懒加载 JSON 对象 // 3. 计数器模式(String key + int value,用混合类型更优) Object2IntOpenHashMap<String> counter = new Object2IntOpenHashMap<>(); counter.addTo("item", 1); // 原子自增,比纯 Object 快 3~5x // 4. 批量插入(FastUtil 独有,适用于 CSV/JSON 加载) String[] keys = {"k1", "k2"}; Object[] values = {new Object(), "val2"}; map.putAll(keys, values, keys.length); // 比 putAll 快 25%~40%3.10 List/Set 使用技巧(String 版)
// 动态 String 列表(比 ArrayList<String> 快 2~4x,内存省 30%) ObjectArrayList<String> list = new ObjectArrayList<>(); list.add("item1"); list.add("item2"); // 快速转数组(零拷贝) String[] array = list.toStringArray(); // 防御性拷贝,推荐 // 从数组创建(零拷贝包装) String[] raw = {"a", "b", "c"}; ObjectArrayList<String> list = ObjectArrayList.wrap(raw); // Set 去重 String(高效哈希) ObjectOpenHashSet<String> set = new ObjectOpenHashSet<>(1_000_000, 0.9f); set.add("unique"); if (set.add("duplicate")) { /* 插入成功 */ } String[] uniqueArray = set.toStringArray();3.11 与 Java Stream 配合(Object 流优化)
Object2ObjectOpenHashMap<String, String> map = ...; // FastUtil 原始迭代器流(比 JDK stream 快 3~7x,无装箱) long count = map.object2ObjectEntrySet() .fastForEach(entry -> total += entry.getKey().length()); // e.g., 统计键长度 // 并行处理(大 String 集合) map.object2ObjectEntrySet().parallelStream() .forEach(entry -> process(entry.getStringKey(), entry.getStringValue()));3.12 序列化注意事项(Object 版)
// Object Map 序列化:用 FastUtil 二进制(比 JDK 快 4~8x) ByteBufferOutput out = ...; Object2ObjectOpenHashMap.writeObject2Object(out, map); // 专为 Object2Object // 反序列化 Object2ObjectOpenHashMap<String, Object> loaded = Object2ObjectOpenHashMap.readObject2Object(in);4. Maven/Gradle 依赖(2025 最新)
<!-- Maven --> <dependency> <groupId>it.unimi.dsi</groupId> <artifactId>fastutil</artifactId> <version>8.5.15</version> </dependency> // Gradle Kotlin DSL implementation("it.unimi.dsi:fastutil:8.5.15")5. 生产环境避坑清单(血泪经验)
坑点 | 正确做法 |
|---|---|
使用new HashMap<Integer,...> | 改用new Int2XxxOpenHashMap() |
map.get(key)返回包装类 | 使用原始方法map.getOrDefault(intKey, 0L) |
List 使用ArrayList | 改用IntArrayList |
for (Integer i : list) | 用for (int i : list)或IntIterator |
序列化超大 Map 超时 | 改用 FastUtil 二进制序列化 API |
并发修改导致异常 | 使用Int2LongOpenHashMap+ 分段锁或外部锁 |
使用new HashMap<String, Object> | 改用new Object2ObjectOpenHashMap<String, Object>()+referenceEquality() |
String key + 原始 value 仍用 Object | 优先Object2IntOpenHashMap(混合优化) |
迭代 Object 集合用 for-each(隐含 equals() 调用) | 用fastIterator()或referenceEquality()加速 |
大 String Set 内存爆炸 | 用ObjectOpenHashSet+ 预估容量 |
null key/value 处理 | FastUtil 默认不支持 null key;用defaultReturnValue()设置默认值 |
与 Lombok/Spring Boot 集成冲突 | 显式导入it.unimi.dsi.fastutil.objects.* |
6. 结论:一条替换原则
只要键或值是原始类型,且预计
size > 10万,就必须使用 FastUtil。对于 String/Object:预计
size > 5万,或频繁 get/put 时,用Object2ObjectOpenHashMap替换 HashMap,提升 20%+ 性能。
记住一句话:
“在 Java 里,装箱是性能杀手,FastUtil 是解药。”
把这篇文章加入你的团队 Wiki,下次代码审查看到HashMap<Integer, ...就直接贴链接。
项目推荐:
https://github.com/vigna/fastutil
快起来吧,你的 CPU 会感谢你!
欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,“长按”或“扫描”下方二维码噢:
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。 谢谢支持哟 (*^__^*)