Java集合 - ArrayList底层源码解析

下面开始对 Java 中 ArrayList 的深度源码分析,基于 JDK 8 的实现(后续版本略有差异,但核心逻辑一致)。我们将从 类结构、扩容机制、核心方法实现、性能优化、线程安全问题 等角度进行详细解析

一、类结构与核心字段

1. 类继承关系

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • AbstractList:提供了 List 接口的部分默认实现(如 get(int index)size() 等)
  • RandomAccess:标记接口,表示支持随机访问(通过索引访问元素,O(1) 时间复杂度)
  • Cloneable:支持克隆
  • Serializable:支持序列化

2. 核心字段

// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组常量(用于无参构造)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 空数组常量(用于指定容量为 0 的构造)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 存储元素的数组
transient Object[] elementData;
// 当前元素个数
private int size;
// 数组最大容量限制(防止溢出)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

关键点

  • elementData:实际存储数据的数组,初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA(空数组),首次添加元素时扩容到默认容量 10
  • size:记录当前集合中实际元素的数量,与 elementData.length 不同。
  • transientelementData 被标记为 transient,但 ArrayList 自定义了 writeObjectreadObject 方法,实现序列化逻辑
  • MAX_ARRAY_SIZE:限制数组最大容量为 Integer.MAX_VALUE - 8,避免 JVM 内存分配异常

.

二、构造方法详解

1. 无参构造函数

public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • 初始化为空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  • 首次添加元素时,会扩容到默认容量 10

2. 带初始容量的构造函数

public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}
}
  • 直接初始化指定容量的数组,避免后续频繁扩容

3. 使用集合初始化

public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} else {this.elementData = EMPTY_ELEMENTDATA;}
}
  • 将集合转换为数组并赋值给 elementData
  • 如果集合返回的数组类型不是 Object[],会强制转换(避免类型问题)

.

三、核心方法源码分析

1. 添加元素:add(E e)

public boolean add(E e) {modCount++; // 修改次数 +1(用于迭代器 fail-fast)add(e, elementData, size);return true;
}private void add(E e, Object[] elementData, int s) {if (s == elementData.length)elementData = grow(); // 扩容elementData[s] = e;size = s + 1;
}
  • 关键步骤:
    1. 检查是否需要扩容(s == elementData.length)
    2. 调用 grow() 方法扩容
    3. 将元素赋值到 elementData[s]
    4. 更新 size

2. 扩容机制:grow()

private Object[] grow() {return grow(size + 1);
}private Object[] grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);     // 1.5 倍扩容if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);return elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 扩容规则:
    • 默认扩容:newCapacity = oldCapacity + (oldCapacity >> 1)(即 1.5 倍)
    • 特殊情况:如果扩容后仍不足(如一次性添加大量元素),直接设置为 minCapacity
    • 最大容量限制:超过 MAX_ARRAY_SIZE 时调用 hugeCapacity() 处理

hugeCapacity(int minCapacity)

private static int hugeCapacity(int minCapacity) {if (minCapacity < 0)throw new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
  • 如果 minCapacity 超过 MAX_ARRAY_SIZE,则返回 Integer.MAX_VALUE,否则返回 MAX_ARRAY_SIZE

3. 删除元素:remove(int index)

public E remove(int index) {Objects.checkIndex(index, size); // 检查索引合法性final Object[] es = elementData;@SuppressWarnings("unchecked") E oldValue = (E) es[index];int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(es, index+1, es, index, numMoved); // 左移元素es[--size] = null; // 帮助 GCreturn oldValue;
}
  • 关键步骤:
    1. 检查索引合法性
    2. 获取目标元素
    3. 将右半部分元素左移(时间复杂度 O(n))
    4. 将最后一个元素置为 null,帮助垃圾回收
    5. 更新 size

4. 获取元素:get(int index)

public E get(int index) {Objects.checkIndex(index, size);return (E) elementData[index];
}
  • 特点:直接通过索引访问数组元素,时间复杂度 O(1)

5. 修改元素:set(int index, E element)

public E set(int index, E element) {Objects.checkIndex(index, size);E oldValue = (E) elementData[index];elementData[index] = element;return oldValue;
}
  • 特点:直接修改数组中指定位置的元素,时间复杂度 O(1)

.

四、性能优化技巧

1. 预估容量,避免频繁扩容

使用 ArrayList(int initialCapacity) 构造函数,提前指定容量。如果容量不足,频繁扩容会导致性能下降(每次扩容需复制数组)

2. 手动扩容:ensureCapacity(int minCapacity)

public void ensureCapacity(int minCapacity) {int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)? 0 : DEFAULT_CAPACITY;if (minCapacity > minExpand) {modCount++;grow(minCapacity);}
}
  • 在添加大量元素前,调用此方法预扩容,减少中间扩容次数

3. 清空集合:clear()

public void clear() {modCount++;for (int i = 0; i < size; i++)elementData[i] = null; // 帮助 GCsize = 0;
}
  • 注意:clear() 不会释放数组空间,只是将 size 置为 0 并将元素设为 null

.

五、线程安全问题

1. 线程不安全

ArrayList 不是线程安全的,多线程环境下可能引发数据不一致或 ConcurrentModificationException

2. 解决方案

  • 使用 Collections.synchronizedList
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
  • 使用 CopyOnWriteArrayList(适合读多写少场景):
List<String> cowList = new CopyOnWriteArrayList<>();

.

六、Fail-Fast 机制

1. 原理

  • ArrayList 通过 modCount 变量记录集合的修改次数
  • 迭代器遍历时会检查 modCount 是否与创建迭代器时的值一致。如果不一致,抛出 ConcurrentModificationException

2. 代码示例

public Iterator<E> iterator() {return new Itr();
}private class Itr implements Iterator<E> {int expectedModCount = modCount;public boolean hasNext() {checkForComodification();return cursor != size;}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}

.

七、性能对比与使用建议

在这里插入图片描述

使用建议

  • 高频随机访问:优先使用 ArrayList
  • 频繁中间插入/删除:使用 LinkedList
  • 线程安全:使用 Collections.synchronizedListCopyOnWriteArrayList

八、其他实用方法

暂时无法在飞书文档外展示此内容

九、总结

暂时无法在飞书文档外展示此内容

十、扩展学习建议

  1. 源码调试:使用 IDE(如 IntelliJ IDEA)逐步调试 ArrayList 的 addremove 等方法,观察 elementDatasize 的变化
  2. 版本差异:对比 JDK 8 和 JDK 17 的 ArrayList 源码,观察扩容策略、subList 实现等细节变化
  3. 进阶主题
  • ArrayListVector 的区别(线程安全 vs 性能)
  • subList 返回的视图对原集合的影响
  • ArrayList 在 JVM 内存模型中的表现(数组的连续性 vs 链表的离散性)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/84760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Qt】Qt控件

文章目录 Qt控件Layout Spacer垂直布局QVBoxLayout水平排列布局QHBoxLayout网格布局 QGridLayout表格布局 QFormLayout Button Contain命令按钮Push Button工具按钮Tool Button单选按钮Radio Button复选框按钮Check Box命令链接按钮Command Link Button按钮盒Button Box组合框G…

PHP基础-运算符

PHP 的运算符是编程中非常基础但又非常重要的一部分&#xff0c;掌握它们能让你更灵活地处理各种逻辑、计算和流程控制。 算术运算符 用于基本数学运算&#xff1a; 运算符含义示例加法$a $b-减法$a - $b*乘法$a * $b/除法$a / $b%取模$a % $b 示例&#xff1a; <?ph…

AR珠宝佩戴与传统的珠宝购物有哪些区别?​

AR 珠宝佩戴与传统的珠宝购物究竟存在着哪些显著区别呢?在传统的珠宝购物模式里&#xff0c;顾客往往需要花费时间和精力前往实体珠宝店。踏入店内&#xff0c;首先映入眼帘的便是那一排排的玻璃展柜&#xff0c;此时&#xff0c;销售人员会热情地走上前&#xff0c;小心翼翼地…

华为云CAE部署spring cloud服务

1 概述 华为云CAE&#xff08;Cloud Application Engine云应用引擎&#xff09;是一个面向WEB、微服务应用的Serverless托管服务&#xff0c;提供极速部署、极低成本、极简运维的一站式应用托管方案。支持从源码、软件包、镜像包快速发布应用&#xff0c;秒级弹性伸缩、按量付…

【技术工具】源码管理 - GIT工具

【技术工具】源码管理 - GIT工具 1 前言 之前参考语雀一位大佬的&#xff0c;但链接找不到了&#xff0c;仅供参考。 1、检查空白错误 //确认将提交的内容中有无空白信息 git diff --check 2、尝试让每一个提交成为一个逻辑的独立变更集 尽量使每笔提交都成为独立的patch&a…

Objective-c Block 面试题

以下是对我们这整段关于 Objective-C 中 Block、__block 修饰符、内存管理行为、生命周期等内容的全面总结&#xff0c;并附带了一套适合面试准备的面试题集&#xff08;带答案&#xff09;。 &#x1f9e0; 一、知识总结&#xff1a;Objective-C Block __block 修饰符 ✅ Bl…

AndroidMJ-基础-05

基础part5: 9:测试相关 postman genemotion espresso 10:性能相关 profiler 9.测试相关 espresso相关&#xff1a; Android Espresso 自动化测试指南&#xff08;Java 版&#xff09;-CSDN博客 10.性能相关 profiler相关&#xff1a; AndroidStudio之内层泄漏工具Profiler…

R语言 | 如何使用R书写html文档?

更灵活的书写方式&#xff0c;可以直接看3. 1. 可用函数 cat()函数writeLines()函数sink()函数重定向输出到HTML文件 小结&#xff1a;cat()适合简单HTML&#xff0c;writeLines()适合多行内容&#xff0c;sink()适合复杂场景。 说明&#xff1a;尽可能不用R包&#xff0c;减…

oracle 表空间超过最大限度,清理数据释放内存

目录 一、扩容&#xff1a;参考 https://blog.csdn.net/weixin_40841731/article/details/134931289 二、清理数据 1、查询文件大小情况&#xff08;管理员账号&#xff09; 2、查询表的大小&#xff08;使用该表空间的用户&#xff09; 3、清理数据&#xff08;使用该表空…

初版BL程序一些细节整理(碎碎念)

一.串口的中断触发 一般我们都是使用TXE或者RXNE来触发中断&#xff0c;其实还有完整传输结束的TC标志位和接收完成的IDLE标志位 这两个标志位有些不同&#xff0c;RXNE标志位只需要读取寄存器就会自行清除&#xff0c;但是这两个需要读取两个&#xff0c;拿IDLE举例子 这里需要…

为何京东与蚂蚁集团竞相申请稳定币牌照?

京东与蚂蚁集团竞相申请稳定币牌照&#xff0c;主要是为了抢占数字金融新赛道&#xff0c;结合香港的宽松监管政策与全球稳定币市场的快速增长。香港2023年推出的稳定币监管框架及2025年8月即将实施的《稳定币条例》&#xff0c;为企业提供了合规路径&#xff0c;吸引京东通过币…

[特殊字符] Harmony OS Next里的Web组件:网页加载的全流程掌控手册

&#x1f389; Harmony OS Next里的Web组件&#xff1a;网页加载的全流程掌控手册 ##Harmony OS Next ##Ark Ts ##教育 本文适用于教育科普行业进行学习&#xff0c;有错误之处请指出我会修改。 开发者必看的生命周期回调详解代码实操指南 作为开发者&#xff0c;你可能经常需…

【Java学习笔记】集合介绍

集合 > > 集合的引出 在之前常使用数组存储数据&#xff0c;存在的问题如下&#xff1a; &#xff08;1&#xff09;初始化时&#xff0c;长度必须指定&#xff0c;而且一旦指定&#xff0c;不能更改 &#xff08;2&#xff09;不方便扩容&#xff08;使用循环复制原…

电流传感器在汽车中的应用:从BMS电池管理到电机控制的工程解析

1 电流传感器&#xff1a;汽车电子系统的神经末梢 在现代汽车电子架构中&#xff0c;电流传感器已从简单的测量元件演变为​​关键的安全与性能组件​​。作为动力系统的“神经末梢”&#xff0c;它们持续采集电流参数并反馈至控制单元&#xff0c;构成​​实时闭环控制的基础…

积分商城拼团系统框架设计

一、逻辑分析 用户相关逻辑 用户注册与登录&#xff1a;用户需要注册账号才能参与积分商城拼团活动。注册过程中需收集必要信息&#xff0c;如用户名、密码、联系方式等。登录功能则用于验证用户身份&#xff0c;方便用户后续操作。用户积分管理&#xff1a;用户通过各种途径&a…

java 数据结构-HashMap

一、hashmap特点 1、HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 2、HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。 3、HashMap 是无序的,即不会记录插入的顺序。 4、HashMa…

DBSyncer:一款开源的数据同步工具

DBSyncer&#xff08;简称 dbs&#xff09;是一款开源的实时数据同步中间件&#xff0c;提供 MySQL、Oracle、SQL Server、PostgreSQL、SQLite、Elasticsearch、Kafka、File、SQL 数据库等同步场景&#xff1b;支持上传插件自定义同步转换业务&#xff1b;提供监控全量和增量数…

大型语言模型的中毒攻击的系统评价

大家读完觉得有帮助记得及时关注和点赞&#xff01;&#xff01;&#xff01; 抽象 随着预训练大型语言模型 &#xff08;LLM&#xff09; 及其训练数据集的广泛使用&#xff0c;人们对与其使用相关的安全风险的担忧显著增加。 这些安全风险之一是 LLM 中毒攻击的威胁&#xff…

Windows 10更新失败解决方法

前言 在我们使用 Windows 时的时候&#xff0c;很多时候遇到系统更新 重启之后却一直提示“我们无法完成更新&#xff0c;正在撤销更改” 这种情况非常烦人&#xff0c;但其实可以通过修改文件的方法解决&#xff0c;并且正常更新到最新版操作系统 01修改注册表 管理员身份…

Redis高级|Redis单线程VS多线程(基础)

文章目录 面试题Redis为什么选择单线程为什么逐渐加入多线程特性Redis6、Redis7的多线程特性和IO多路复用入门Redis7多线程 面试题 Redis到底是单线程还是多线程&#xff1f;IO多路复用听说过吗&#xff1f;Redis为什么这么快&#xff1f; Redis为什么选择单线程 其实Redis单…