java-arraylist 源码分析 1

深入分析 Java 中的 `ArrayList` 源码

`ArrayList` 是 Java 集合框架中的一个重要类,它基于数组实现,提供了动态数组的功能。`ArrayList` 是一个非常常用的集合类,因为它在随机访问和遍历方面性能优越。本文将详细分析 `ArrayList` 的源码,包括其数据结构、构造方法、核心操作、自动扩容机制等。

1. `ArrayList` 的基本数据结构

`ArrayList` 是一个动态数组,它的底层是一个 `Object` 类型的数组。在 `ArrayList` 的实现中,主要使用以下几个关键字段:

```java

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

private static final long serialVersionUID = 8683452581122892189L;

// 默认初始容量

private static final int DEFAULT_CAPACITY = 10;

// 空数组实例,当初始容量为0时使用

private static final Object[] EMPTY_ELEMENTDATA = {};

// 默认空数组实例,当使用默认构造函数时使用

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 存储元素的数组

transient Object[] elementData;

// 元素数量

private int size;

}

```

  • `elementData`:这是实际存储元素的数组。

  • `size`:这是当前 `ArrayList` 中包含的元素数量。

  • `DEFAULT_CAPACITY`:这是默认的初始容量。

  • `EMPTY_ELEMENTDATA` 和 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA`:分别是用于初始化时的空数组。

2. 构造方法

`ArrayList` 提供了多个构造方法:

2.1 默认构造方法

```java

public ArrayList() {

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

```

默认构造方法将 `elementData` 初始化为 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA`,即一个空数组。当第一次添加元素时,数组将扩展到默认初始容量。

2.2 指定初始容量的构造方法

```java

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` 的初始容量。如果初始容量大于 0,则创建一个相应大小的数组;如果初始容量为 0,则使用空数组;如果初始容量为负数,则抛出 `IllegalArgumentException`。

2.3 从另一个集合创建 `ArrayList`

```java

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;

}

}

```

此构造方法从另一个集合创建 `ArrayList`,并将集合中的所有元素添加到 `ArrayList` 中。

3. 核心操作方法

3.1 添加元素

添加元素的方法主要有 `add(E e)` 和 `add(int index, E element)`:

```java

public boolean add(E e) {

ensureCapacityInternal(size + 1); // 扩容检查

elementData[size++] = e;

return true;

}

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++;

}

```

  • `add(E e)`:在数组末尾添加元素,需要先检查容量是否足够,如果不足,则进行扩容。

  • `add(int index, E element)`:在指定位置插入元素,需要先检查索引的有效性,然后将指定位置后的元素向后移动,再插入新元素。

3.2 确保容量

`ensureCapacityInternal(int minCapacity)` 方法用于确保数组有足够的容量,如果不足则进行扩容:

```java

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

private static int calculateCapacity(Object[] elementData, int minCapacity) {

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

return Math.max(DEFAULT_CAPACITY, minCapacity);

}

return minCapacity;

}

```

4. 自动扩容机制

当 `ArrayList` 中的元素超过当前数组的容量时,需要进行扩容。`grow(int minCapacity)` 方法用于扩容:

```java

private void grow(int minCapacity) {

int oldCapacity = elementData.length;

int newCapacity = oldCapacity + (oldCapacity >> 1);

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

elementData = Arrays.copyOf(elementData, newCapacity);

}

private static int hugeCapacity(int minCapacity) {

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return (minCapacity > MAX_ARRAY_SIZE) ?

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

```

  • `oldCapacity`:旧容量,即扩容前数组的长度。

  • `newCapacity`:新容量,为旧容量的 1.5 倍。

  • 如果 `newCapacity` 仍小于所需的最小容量,则将其设为 `minCapacity`。

  • 如果 `newCapacity` 超过了 `MAX_ARRAY_SIZE`,则调用 `hugeCapacity(int minCapacity)` 方法进行处理。

  • 最后通过 `Arrays.copyOf` 方法将旧数组复制到新数组中。

5. 删除元素

`ArrayList` 提供了删除元素的方法 `remove(int index)` 和 `remove(Object o)`:

```java

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; // clear to let GC do its work

return oldValue;

}

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; // clear to let GC do its work

}

```

  • `remove(int index)`:根据索引删除元素,首先检查索引的有效性,然后通过 `System.arraycopy` 方法将索引后的元素向前移动,覆盖要删除的元素。

  • `remove(Object o)`:根据对象删除元素,通过遍历找到目标元素并调用 `fastRemove(int index)` 方法进行删除。

6. 获取元素和修改元素

`ArrayList` 提供了获取元素的方法 `get(int index)` 和修改元素的方法 `set(int index, E element)`:

```java

public E get(int index) {

rangeCheck(index);

return elementData(index);

}

public E set(int index, E element) {

rangeCheck(index);

E oldValue = elementData(index);

elementData[index] = element;

return oldValue;

}

```

  • `get(int index)`:根据索引获取元素,首先检查索引的有效性,然后返回数组中的对应元素。

  • `set(int index, E element)`:根据索引修改元素,首先检查索引的有效性,然后将指定位置的元素替换为新元素,并返回旧元素。

相关推荐
纪莫16 分钟前
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
java·java面试⑧股
JavaGuide1 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端
用户3721574261351 小时前
Java 轻松批量替换 Word 文档文字内容
java
白鲸开源1 小时前
教你数分钟内创建并运行一个 DolphinScheduler Workflow!
java
CoovallyAIHub2 小时前
中科大DSAI Lab团队多篇论文入选ICCV 2025,推动三维视觉与泛化感知技术突破
深度学习·算法·计算机视觉
Java中文社群2 小时前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心2 小时前
从零开始学Flink:数据源
java·大数据·后端·flink
间彧2 小时前
Spring Boot项目中如何自定义线程池
java
间彧2 小时前
Java线程池详解与实战指南
java
用户298698530142 小时前
Java 使用 Spire.PDF 将PDF文档转换为Word格式
java·后端