网站首页 > 技术文章 正文
在 Java 丰富的类库中,ArrayList 犹如一颗璀璨的明珠,为开发者提供了便捷高效的数据存储和操作方式。让我们一同揭开它源码的神秘面纱,探索其内部的精妙机制。
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.RandomAccess;
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
// 表示空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 另一种表示空数组,用于特定情况
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 用于存储元素的数组
transient Object[] elementData;
// 列表中实际元素的数量
private int size;
// 无参构造函数,初始化时使用特定的空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 带初始容量参数的构造函数
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);
}
}
// 基于另一个集合创建 ArrayList 的构造函数
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length)!= 0) {
// 确保数组类型是 Object[]
if (elementData.getClass()!= Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
// 获取列表中元素的数量
@Override
public int size() {
return size;
}
// 判断列表是否为空
@Override
public boolean isEmpty() {
return size == 0;
}
// 判断列表是否包含指定元素
@Override
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
// 查找指定元素在列表中的索引,如果不存在则返回 -1
@Override
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i] == null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 查找指定元素在列表中最后出现的索引,如果不存在则返回 -1
@Override
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size - 1; i >= 0; i--)
if (elementData[i] == null)
return i;
} else {
for (int i = size - 1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 获取指定索引位置的元素
@Override
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
// 检查索引是否合法
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 根据索引获取元素,并进行类型转换
E elementData(int index) {
return (E) elementData[index];
}
// 将列表转换为数组
@Override
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
// 将列表元素复制到指定的数组中
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// 如果给定的数组长度不够,创建一个新的合适长度的数组
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
// 设置指定索引位置的元素值
@Override
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
// 在指定索引位置添加元素
@Override
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // 检查并确保容量足够
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
// 检查添加元素时索引的合法性
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 向列表末尾添加元素
@Override
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保容量足够
elementData[size++] = e;
return true;
}
// 确保内部容量足够,如果不够则进行扩容
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
// 明确地确保容量足够,如果需要则进行扩容操作
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 扩容方法
private void grow(int minCapacity) {
// 获取当前容量
int oldCapacity = elementData.length;
// 新容量为旧容量的 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 复制元素到新的扩容后的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 从列表中删除指定索引位置的元素
@Override
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // 让垃圾回收器可以回收
return oldValue;
}
// 根据元素值删除列表中的第一个匹配元素
@Override
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
// 快速删除指定索引位置的元素
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // 让垃圾回收器可以回收
}
// 清空列表
@Override
public void clear() {
modCount++;
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
// 克隆方法
@Override
public ArrayList<E> clone() {
try {
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
}
关键要点解析:
- 成员变量:DEFAULT_CAPACITY = 10:定义了默认的初始容量,当创建 ArrayList 时如果未指定初始容量,且后续添加元素导致需要扩容时,会以 10 为初始容量进行扩容。EMPTY_ELEMENTDATA = {} 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}:用于表示空数组,在不同的初始化场景中使用。elementData:实际存储元素的数组。size:记录数组中实际存储的元素数量。
- 构造函数:无参构造函数中,将 elementData 初始化为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,表示初始时数组为空,后续添加元素时再进行容量分配。带初始容量参数的构造函数,根据传入的容量值创建相应大小的数组,如果容量值不合法则抛出异常。基于另一个集合创建的构造函数,将传入集合的元素复制到新的数组中,并处理数组类型的转换。
- 添加元素:add 方法:首先调用 ensureCapacityInternal 方法确保有足够的容量来添加新元素。ensureCapacityInternal 方法:如果当前数组是默认的空数组,会将所需最小容量与默认容量比较并取较大值。然后调用 ensureExplicitCapacity 方法。ensureExplicitCapacity 方法:检查所需容量是否超过当前数组长度,如果是则调用 grow 方法进行扩容。grow 方法:计算新的容量(通常为原容量的 1.5 倍),并通过 Arrays.copyOf 方法将原数组元素复制到新的扩容后的数组。
- 获取元素:get 方法:通过 rangeCheck 方法检查索引是否合法,然后通过数组索引直接获取元素,并进行类型转换返回。
- 删除元素:remove 方法:通过 rangeCheck 方法检查索引合法性。然后将指定索引后面的元素向前移动,最后将数组末尾的元素置为 null 以方便垃圾回收,并减少数组的实际元素数量。
- 迭代器的快速失败机制:内部有一个 modCount 变量,在对集合进行结构修改操作(如添加、删除元素)时会增加其值。迭代器在遍历过程中会检查 modCount 是否被修改,如果被修改则抛出 ConcurrentModificationException 异常,以保证在迭代过程中集合的结构不被意外修改。
ArrayList 适用于需要快速随机访问和频繁添加 / 删除元素末尾操作的场景,但在频繁插入和删除元素中间位置时,可能会有性能开销,因为需要移动大量元素。
猜你喜欢
- 2024-10-08 java常用数据判空、比较和类型转换
- 2024-10-08 集合框架-ArrayList源码分析(java集合框架源码解析)
- 2024-10-08 大数据编程入门:Java ArrayList(java大数据视频教程)
- 2024-10-08 Python开发入门之列表-List(python列表的基本操作编程)
- 2024-10-08 如何在python各种列表中求最值?(如何在python各种列表中求最值的方法)
- 2024-10-08 List接口常用方法(list接口的常用方法)
- 2024-10-08 Python语法基础(6)集合(python中的集合)
- 2024-10-08 Java初学者学习任务总结「15」(java学习知识点路线)
- 2024-10-08 Sonar代码规范分析(sonar代码扫描规则及解决方案)
- 2024-10-08 最详细集合源码解析之ArrayList集合源码解析
- 10-02基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- 10-02Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- 10-02AWD-LSTM语言模型是如何实现的_lstm语言模型
- 10-02NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 10-02使用ONNX和Torchscript加快推理速度的测试
- 10-02tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- 10-02Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- 10-02Gateway_gateways
- 最近发表
-
- 基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- AWD-LSTM语言模型是如何实现的_lstm语言模型
- NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 使用ONNX和Torchscript加快推理速度的测试
- tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- Gateway_gateways
- Coze开源本地部署教程_开源canopen
- 扣子开源本地部署教程 丨Coze智能体小白喂饭级指南
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)