Java中HashMap精简序列化需重写writeObject和readObject方法:跳过null桶,仅序列化有效节点及其引用字段,并按哈希值重建table结构。

Java 中 HashMap 序列化时默认会把整个 table 数组(含大量 null 桶)一并写入,造成体积膨胀和反序列化冗余。要只保存真正存储了键值对的“有效数据”,核心是绕过默认序列化逻辑,通过自定义 writeObject 和 readObject 方法,精准控制哪些节点被写出。
重写 writeObject:跳过空桶,只遍历非空链/树
HashMap 本身已声明 table 为 transient,不会被默认序列化。你只需在子类或代理类中重写私有方法 writeObject:
- 先调用
defaultWriteObject(),确保size、modCount等关键字段被序列化 - 再遍历
table数组,对每个非null桶(即tab[i] != null),递归写出其链表或红黑树中的全部节点 - 每个节点需显式写出
hash、key、value,以及链表的next或树节点的left/right等必要引用字段
配套实现 readObject:按需重建 table 结构
反序列化必须与写入逻辑严格对应:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 先
defaultReadObject()恢复基础字段 - 根据读出的节点数量和哈希值,重新计算桶索引(
(n - 1) & hash),逐个构造Node并插入到新分配的table中 - 不依赖
put方法(可能触发额外 rehash 或回调),而是直接调用底层putVal或手动构建节点链
不改源码的实用优化策略
若无法修改类定义,可通过外部手段降低空桶比例,间接提升序列化效率:
- 构造时指定合理初始容量,例如预估元素数 ÷ 0.75 后向上取最接近的 2 的幂
- 序列化前调用
trimToSize()(需自行实现或反射调用resize()),但注意这会触发 rehash,慎用于高频场景 - 业务允许时,临时转为
LinkedHashMap再序列化——它内部无稀疏数组,结构更紧凑,只是失去 O(1) 查找优势
本质上,精简序列化不是靠配置开关,而是靠接管序列化钩子函数,让每一步都只处理真实存在的数据节点。JDK 自身的 HashMap 就是这样实现的,你只需复用其思路即可。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










