当前位置: 首页 > web >正文

ArrayList源码解析之序列化

引子

我们都知道 ArrayList 是通过 数组 来存储元素的。阅读 ArrayList 的源码,会发现:

transient Object[] elementData;

也就是说,用来存放元素的数组 elementDatatransient 修饰了。按道理,transient 的意思是“这个字段不要被序列化”,那么问题就来了——ArrayList 里的元素难道不需要被序列化吗?那它是怎么把数据存下来的?

带着这个疑问,我们继续往下看。


源码揭秘

首先确认一下,elementData 确实是 transient 的:

transient Object[] elementData; 

那它的数据是怎么保存和恢复的呢?答案就是:
ArrayList 自己实现了一套序列化和反序列化方法,也就是 writeObjectreadObject

自定义序列化

@java.io.Serial
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {int expectedModCount = modCount;// 先写入非 transient 字段,比如 sizes.defaultWriteObject();// 写 size(元素数量),注意不是数组的容量s.writeInt(size);// 写入真正存储的元素for (int i = 0; i < size; i++) {s.writeObject(elementData[i]);}// 并发修改检测if (modCount != expectedModCount) {throw new ConcurrentModificationException();}
}

自定义反序列化

@java.io.Serial
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// 先恢复非 transient 字段s.defaultReadObject();  // capacity,这里直接忽略掉s.readInt(); if (size > 0) {// 按 size 来分配数组,而不是之前的容量Object[] elements = new Object[size];// 把序列化时保存的元素一个个读回来for (int i = 0; i < size; i++) {elements[i] = s.readObject();}elementData = elements;} else if (size == 0) {elementData = EMPTY_ELEMENTDATA;} else {throw new java.io.InvalidObjectException("Invalid size: " + size);}
}

实现解析

从代码可以看出,ArrayList 在序列化时只关心 真实存放的元素,而不是整个数组容量。

为什么这么做?举个例子:

ArrayList<String> list = new ArrayList<>(1000);
list.add("A");

这个时候,elementData 的数组长度可能是 1000,但实际上只存了一个元素 "A"
如果不加 transient,直接把整个数组序列化,那 999 个 null 也会被写到文件里,结果就是 空间浪费 + 性能下降

所以,JDK 作者就用了一个取巧的办法:

  • elementData 标记为 transient,让它不参与默认序列化。
  • writeObject 里只把 size 个真实元素写出去。
  • readObject 时再用 size 来重建数组,把元素填回去。

这样既节省空间,也让 ArrayList 的序列化行为更合理。


总结

  1. ArrayList 的底层存储数组 elementDatatransient 修饰,不会直接参与序列化。
  2. ArrayList 自己实现了 writeObject / readObject,只序列化实际存储的元素,而不是整个数组容量。
  3. 这样做的好处是:
    • 避免序列化过程中保存大量无效的 null
    • 提升序列化和反序列化效率,节省存储空间。
    • ArrayList 在不同 JDK 版本的扩容策略下,依然保持序列化兼容性。

一句话总结:ArrayList 的序列化是“只保存有用的元素,不浪费空间”

http://www.xdnf.cn/news/19507.html

相关文章:

  • 【LeetCode 热题 100】64. 最小路径和——(解法二)递推
  • DSPFilters实现低通滤波器(QT)
  • 【开题答辩全过程】以 留守儿童志愿者服务系统为例,包含答辩的问题和答案
  • Java全局异常处理器:优雅处理系统异常
  • 数学运算符号:跨越千年的智慧结晶与文明印记
  • strtok()字符串分隔函数
  • VideoPoet:Google发布的用于视频生成的大语言模型
  • 【C#】在一个任意旋转的矩形(由四个顶点定义)内绘制一个内切椭圆
  • SpringAI应用开发面试实录:核心技术、架构设计与业务场景全解析
  • 华为研发投资与管理实践(IPD)读书笔记
  • VSCode `tasks.json` 中 `tasks` 数组的详细解析
  • 语义分析:从读懂到理解的深度跨越
  • Photoshop - Ps 标尺
  • JVM参数配置调优指南
  • 在开发过程中经常遇到 OOM(内存溢出)问题,如何解决?
  • 解决IDEA 2025.2升级报错:Scannning Files to Index卡住问题分析与修复
  • 设计模式:外观模式(Facade Pattern)
  • 【Proteus仿真】开关控制系列仿真——开关控制LED/拨码开关二进制计数/开关和继电器控制灯灭
  • 第3章 乱码的前世今生-字符集和比较规则
  • 常见线程池的创建方式及应用场景
  • 将基于 Spring Boot 3.0.0 的 JavaWeb 应用部署到腾讯云并配置域名
  • Iterative loop of ML development|机器学习的迭代发展
  • C#基础(②音乐播发器MCI(Media Control Interface))
  • MySQL 常用语法
  • PortSwigger靶场之Stored XSS into HTML context with nothing encoded通关秘籍
  • Spring Boot 3.x 微服务架构实战指南
  • k8s中 discovery-token和token 的区别
  • Openstack Eproxy 2025.1 安装指南
  • 基于OpenCv做照片分析应用一(Java)
  • 学习记录(二十二)--Overleaf中生成的PDF左上角1.5em问题