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

Java高频面试之集合-04

hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶

面试官:ArrayList 怎么序列化的? 为什么用 transient 修饰数组?

ArrayList 的序列化机制与 transient 修饰数组的深层原因


一、ArrayList 的序列化实现

Java 的序列化机制默认会序列化对象的 所有非 transient 字段。但 ArrayList 的内部实现中,存储元素的数组 elementDatatransient 修饰,这意味着它不会被默认序列化。
为了解决这个问题,ArrayList 自定义了序列化逻辑,仅序列化实际存储的元素,而不是整个数组。以下是关键步骤:

1. 自定义 writeObject 方法

ArrayList 通过重写 writeObject 方法,手动控制序列化过程:

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // 1. 写入默认的非transient字段(如size、modCount)
    s.defaultWriteObject(); 

    // 2. 写入数组的实际容量(可能大于size)
    s.writeInt(size); 

    // 3. 仅序列化有效元素(从0到size-1),跳过未使用的数组槽位
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }
}
  • 优化点:避免序列化整个 elementData 数组(可能包含未使用的 null 值),减少序列化后的数据大小。
2. 自定义 readObject 方法

反序列化时,ArrayList 通过 readObject 方法重建数组:

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // 1. 读取默认的非transient字段
    elementData = EMPTY_ELEMENTDATA;
    s.defaultReadObject();

    // 2. 读取实际元素数量(size)
    s.readInt();

    if (size > 0) {
        // 3. 根据size创建新数组(避免预分配多余空间)
        elementData = new Object[size];

        // 4. 逐个读取元素,填充数组
        for (int i=0; i<size; i++) {
            elementData[i] = s.readObject();
        }
    }
}
  • 优化点:反序列化后,elementData 的容量严格等于 size,避免空间浪费。

二、为什么用 transient 修饰 elementData 数组?

transient 关键字的作用是 阻止字段被默认序列化ArrayList 使用 transient 修饰 elementData 的原因主要有以下几点:

1. 避免序列化冗余数据
  • 问题背景
    ArrayListelementData 数组长度通常大于当前元素数量(size)。例如,初始容量为 10,但仅存储了 5 个元素时,剩余的 5 个槽位为 null
  • 直接序列化的后果
    如果直接序列化整个数组,会写入大量无效的 null 值,显著增加序列化后的数据大小(尤其在大容量集合中)。
2. 减少网络传输与存储开销
  • 自定义序列化的优势
    通过仅序列化有效元素(0size-1),数据量减少到最小。例如:
    ArrayList<Integer> list = new ArrayList<>(100);
    list.add(1);
    list.add(2);
    
    • 默认序列化:写入长度为 100 的数组(含 98 个 null)。
    • 自定义序列化:仅写入 2 个有效元素。
3. 避免反序列化后的空间浪费
  • 默认反序列化的问题
    如果直接反序列化整个数组,重建后的 elementData 可能包含大量未使用的空间(例如容量 100,但实际仅需 2 个元素)。
  • 自定义反序列化的优化
    反序列化时,根据 size 创建容量精确等于元素数量的数组,确保内存高效利用。

三、对比默认序列化与自定义序列化
场景默认序列化(无 transient自定义序列化(transient + writeObject
序列化数据大小包含未使用槽位的 null,数据量大仅包含有效元素,数据量小
反序列化后数组容量与序列化前一致(可能远大于实际元素数量)等于元素数量(size
网络传输效率低(传输冗余数据)高(仅传输有效数据)
内存占用可能浪费内存(未使用的数组槽位)严格按需分配内存

四、源码示例解析

ArrayListwriteObjectreadObject 为例,进一步理解设计思想:

// ArrayList.java
public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    // 存储元素的数组,被transient修饰
    transient Object[] elementData;

    // 自定义序列化逻辑
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        s.defaultWriteObject(); // 写入非transient字段(如size)
        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 = EMPTY_ELEMENTDATA;
        s.defaultReadObject(); // 读取非transient字段(如size)
        int capacity = s.readInt(); // 读取元素数量
        if (capacity > 0) {
            elementData = new Object[capacity]; // 创建精确容量的数组
            for (int i=0; i<capacity; i++) {
                elementData[i] = s.readObject(); // 填充元素
            }
        }
    }
}

五、🌶️
  • 为什么用 transient
    避免序列化 elementData 中的冗余数据(未使用的 null 槽位),显著优化序列化后的数据大小和传输效率。
  • 如何实现序列化
    通过重写 writeObjectreadObject,仅序列化有效元素,并在反序列化时按需重建数组。
  • 设计思想
    在保持集合功能的前提下,通过精细控制序列化过程,实现空间与性能的最优平衡。

在这里插入图片描述

相关文章:

  • Nginx 开启Baise认证
  • 计算机二级MS之PPT
  • 数据库系统概论(一)详细介绍数据库与基本概念
  • 微服务概览与治理
  • 零售交易流程相关知识(top-down拆解)
  • 同步 Fork 仓库的命令
  • 公开笔记:自然语言处理(NLP)中文文本预处理主流方法
  • 软考高级信息系统项目管理师笔记-第12章项目质量管理
  • 【Manus资料合集】激活码内测渠道+《Manus Al:Agent应用的ChatGPT时刻》(附资源)
  • 布隆过滤器原理详解:高效解决大规模数据去重与查询问题
  • Mysql安装方式
  • Java 8 四大函数式接口详解
  • HOW - React 如何在在浏览器绘制之前同步执行 - useLayoutEffect
  • 数据结构拓展:详解realloc(C++)
  • K8S学习之基础十四:k8s中Deployment控制器概述
  • 推荐一个基于Koin, Ktor Paging等组件的KMM Compose Multiplatform项目
  • 机器学习之无监督学习
  • 一文解锁基于 MemryX 与 RK3588 的边缘 AI “王炸” 组合解决方案
  • 【网络安全】——协议逆向与频繁序列提取:从流量中解码未知协议
  • BasicToolNode(tools=[search_tool, lookup_policy, query_sqldb])的内部执行逻辑
  • 企业品牌网站有哪些/百度官方人工客服电话
  • wordpress 安装主题 无法创建目录/关键词优化
  • 专门做教育咨询有限公司网站/域名申请的流程
  • 做网站能做职业吗/西安百度推广开户多少钱
  • 男女做那个能看的视频网站/宁波seo外包推广公司
  • 外贸网站为何搜不到/企业网站模板设计