面试题:简述为什么 ArrayList 的 elementData 加上 transient 修饰?

在 ArrayList 的源码中,elementData 数组被声明为 transient,即:

transient Object[] elementData;

transient 关键字的作用是 阻止该字段被默认的序列化机制序列化ArrayList 使用 transient 修饰 elementData 的原因如下:


1. 优化序列化性能

  • elementData 是 ArrayList 内部用于存储元素的数组,它的长度通常会比实际存储的元素数量大(因为 ArrayList 会预留一些空间以支持动态扩容)。
  • 如果直接序列化整个 elementData 数组,会导致序列化的数据量变大,因为数组中未使用的部分(即 null 值)也会被序列化。
  • 通过 transient 修饰 elementData,可以避免序列化未使用的数组空间,从而减少序列化后的数据大小,提高性能。

2. 自定义序列化逻辑

  • ArrayList 通过实现 java.io.Serializable 接口并重写 writeObject 和 readObject 方法,自定义了序列化和反序列化的逻辑。
  • 在 writeObject 方法中,ArrayList 只序列化实际存储的元素,而不是整个 elementData 数组。
  • 在 readObject 方法中,ArrayList 会根据序列化的元素数量重新构建 elementData 数组。

示例代码(简化版):

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // 只序列化实际存储的元素
    s.defaultWriteObject();
    s.writeInt(size); // 写入实际元素数量
    for (int i = 0; i < size; i++) {
        s.writeObject(elementData[i]); // 写入每个元素
    }
}

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // 读取实际元素数量并重建 elementData 数组
    s.defaultReadObject();
    int capacity = s.readInt(); // 读取实际元素数量
    elementData = new Object[capacity];
    for (int i = 0; i < capacity; i++) {
        elementData[i] = s.readObject(); // 读取每个元素
    }
}

3. 节省存储空间

  • 由于 elementData 数组的长度通常会大于实际存储的元素数量,直接序列化整个数组会浪费存储空间。
  • 通过自定义序列化逻辑,只序列化实际存储的元素,可以显著减少序列化后的数据大小,节省存储空间。

4. 总结

  • transient 修饰 elementData 是为了避免序列化未使用的数组空间,从而优化序列化性能和节省存储空间。
  • ArrayList 通过自定义 writeObject 和 readObject 方法,实现了只序列化实际存储的元素,而不是整个数组。

5. 示例

假设有一个 ArrayList,其 elementData 数组长度为 10,但只存储了 5 个元素:

  • 如果直接序列化 elementData,会序列化 10 个元素(包括 5 个 null 值)。
  • 通过自定义序列化逻辑,只序列化 5 个实际存储的元素,从而减少数据量。

6. 扩展

  • transient 关键字的作用是阻止字段被默认的序列化机制序列化,通常用于优化序列化性能或保护敏感数据。
  • 除了 ArrayList,其他集合类(如 HashMap)也使用了类似的机制来优化序列化。
THE END
点赞13 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容