ArrayList源码解析之序列化

引子

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

css 复制代码
transient Object[] elementData;

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

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


源码揭秘

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

css 复制代码
transient Object[] elementData; 

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

自定义序列化

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

自定义反序列化

arduino 复制代码
@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 在序列化时只关心 真实存放的元素,而不是整个数组容量。

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

ini 复制代码
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 的序列化是"只保存有用的元素,不浪费空间"

相关推荐
铁蛋AI编程实战14 分钟前
通义千问 3.5 Turbo GGUF 量化版本地部署教程:4G 显存即可运行,数据永不泄露
java·人工智能·python
晚霞的不甘25 分钟前
CANN 编译器深度解析:UB、L1 与 Global Memory 的协同调度机制
java·后端·spring·架构·音视频
SunnyDays101127 分钟前
使用 Java 冻结 Excel 行和列:完整指南
java·冻结excel行和列
摇滚侠38 分钟前
在 SpringBoot 项目中,开发工具使用 IDEA,.idea 目录下的文件需要提交吗
java·spring boot·intellij-idea
爱敲代码的TOM43 分钟前
数据结构总结
数据结构
云姜.43 分钟前
java多态
java·开发语言·c++
李堇1 小时前
android滚动列表VerticalRollingTextView
android·java
泉-java1 小时前
第56条:为所有导出的API元素编写文档注释 《Effective Java》
java·开发语言
zfoo-framework2 小时前
帧同步和状态同步
java
charlotte102410242 小时前
高并发:关于在等待学校教务系统选课时的碎碎念
java·运维·网络