在 Java 集合框架中,ArrayList
是使用最广泛的集合之一。它是基于数组实现的动态数组,具有自动扩容的功能。扩容机制是 ArrayList
的一个重要特性,它让 ArrayList
能够应对动态变化的元素数量。接下来,我们将详细解读 ArrayList
的扩容机制,并结合源码进行说明。
源码解析
我们从 ArrayList
的构造函数和 add
方法入手,逐步揭示其扩容机制。
构造函数
ArrayList
有多个构造函数,其中最常用的是无参构造函数和带初始容量的构造函数。
java
// 无参构造函数
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);
}
}
无参构造函数会将 elementData
初始化为一个默认空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
。而带初始容量的构造函数则根据提供的容量大小初始化数组。
add 方法
当我们向 ArrayList
中添加元素时,add
方法会检查是否需要扩容:
java
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 核心方法,确保容量
elementData[size++] = e;
return true;
}
ensureCapacityInternal
方法是扩容机制的核心:
java
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
当 minCapacity
超过当前数组长度时,会调用 grow
方法进行扩容。
grow 方法
grow
方法是扩容的关键部分:
java
private static final int DEFAULT_CAPACITY = 10;
private void 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);
elementData = Arrays.copyOf(elementData, newCapacity);
}
扩容的策略是将容量增加到原来的 1.5 倍 (oldCapacity + (oldCapacity >> 1)
),同时确保新容量不小于 minCapacity
。如果新容量超过了最大数组大小 MAX_ARRAY_SIZE
,则调用 hugeCapacity
方法进行特殊处理。
扩容机制设计的目的
扩容机制的设计目的是为了在动态添加元素时,避免频繁的内存分配和数据复制。通过按需扩容,ArrayList
能够高效地管理内存,同时保持灵活性。
性能对比
我们可以通过以下代码示例来对比扩容机制的性能:
java
import java.util.ArrayList;
public class ArrayListPerfTest {
public static void main(String[] args) {
// 预先指定容量的 ArrayList
long startTime = System.nanoTime();
ArrayList<Integer> listWithCapacity = new ArrayList<>(1000000);
for (int i = 0; i < 1000000; i++) {
listWithCapacity.add(i);
}
long endTime = System.nanoTime();
System.out.println("ArrayList with initial capacity: " + (endTime - startTime) + " ns");
// 默认构造函数的 ArrayList
startTime = System.nanoTime();
ArrayList<Integer> listWithoutCapacity = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
listWithoutCapacity.add(i);
}
endTime = System.nanoTime();
System.out.println("ArrayList without initial capacity: " + (endTime - startTime) + " ns");
}
}
运行上述代码可以发现,预先指定容量的 ArrayList
性能更好,因为它避免了多次扩容操作。
实际应用场景
- 动态数据集合:在需要频繁添加元素的场景中,
ArrayList
的扩容机制可以保证集合的动态性和灵活性。 - 大数据处理:在处理大数据集时,预先指定容量可以提高性能,减少扩容带来的开销。