引言
在 Java 集合框架中,ArrayList
是最常用的集合之一。它是一个动态数组,提供了灵活的大小调整功能,适用于需要频繁访问和修改元素的场景。本文将详细介绍 ArrayList
的特点、使用方法、实现原理及其源码解析,帮助读者深入理解 ArrayList
的内部机制。
1. ArrayList 简介
ArrayList
是 Java 集合框架中的一个类,实现了 List
接口。它是一个基于数组的动态数据结构,可以自动调整大小以适应元素的添加和删除操作。ArrayList
允许存储重复的元素和 null
元素,并且保证元素的插入顺序。
1.1 特点
- 动态数组:
ArrayList
可以根据需要动态调整其容量。 - 随机访问:支持高效的随机访问,时间复杂度为 O(1)。
- 允许重复元素和 null 元素。
- 线程不安全:
ArrayList
不是线程安全的,需要在多线程环境中进行额外的同步处理。
2. ArrayList 的使用
2.1 基本操作
以下是一些常用的 ArrayList
操作示例:
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
// 创建一个 ArrayList
ArrayList<String> list = new ArrayList<>();
// 添加元素
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 访问元素
System.out.println("Element at index 1: " + list.get(1));
// 修改元素
list.set(1, "Blueberry");
System.out.println("Modified list: " + list);
// 删除元素
list.remove(0);
System.out.println("After removal: " + list);
// 遍历元素
for (String fruit : list) {
System.out.println(fruit);
}
}
}
2.2 常用方法
add(E e)
:在列表末尾添加元素。add(int index, E element)
:在指定位置插入元素。get(int index)
:获取指定位置的元素。set(int index, E element)
:替换指定位置的元素。remove(int index)
:移除指定位置的元素。size()
:返回列表的元素个数。isEmpty()
:判断列表是否为空。contains(Object o)
:判断列表是否包含指定元素。
3. ArrayList 的实现原理
ArrayList
是基于数组实现的,其核心在于一个动态扩展的数组。当数组容量不足时,ArrayList
会自动扩展,以容纳更多元素。
3.1 底层数据结构
ArrayList
使用一个数组 elementData
来存储元素:
transient Object[] elementData;
3.2 动态扩展机制
当添加元素时,如果数组容量不足,ArrayList
会创建一个更大的新数组,并将旧数组中的元素复制到新数组中。默认情况下,扩展后的新数组容量是旧容量的 1.5 倍。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
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);
}
4. ArrayList 的源码解析
4.1 构造方法
ArrayList
提供了多种构造方法:
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);
}
}
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;
}
}
4.2 添加元素
add(E e)
方法会将元素添加到数组末尾,并确保数组容量足够:
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
add(int index, E element)
方法会在指定位置插入元素,并移动后续元素:
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++;
}
4.3 删除元素
remove(int index)
方法会移除指定位置的元素,并移动后续元素:
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;
}
5. 性能分析
5.1 时间复杂度
- 添加元素:向末尾添加元素的时间复杂度为 O(1),在容量不足时需要扩展数组,最坏情况下时间复杂度为 O(n)。
- 访问元素:随机访问元素的时间复杂度为 O(1)。
- 删除元素:删除元素的时间复杂度为 O(n),因为需要移动后续元素。
5.2 空间复杂度
ArrayList
的空间复杂度主要取决于其容量。默认情况下,ArrayList
会预留一定的空间,以减少扩展数组的频率。扩展后的新数组容量是旧容量的 1.5 倍,因此在扩展过程中会有一定的空间浪费。
6. 使用场景
ArrayList
适用于以下场景:
- 需要频繁随机访问元素的场景,如实现缓存。
- 需要动态调整数组大小的场景,如实现动态数据结构。
- 需要快速添加和删除末尾元素的场景,如实现栈(Stack)。
结论
ArrayList
是 Java 集合框架中非常重要的一个类,提供了动态数组的功能。通过对其特点、使用方法、实现原理及源码的详细解析,我们可以更好地理解 ArrayList
的内部机制,并在实际开发中合理使用 ArrayList
,提高程序的性能和可维护性。希望本文能帮助读者深入理解 ArrayList
,并在实际项目中灵活应用。