ArrayList 核心特性
| 特性 | 說明 |
| 底層結構 | 動態數組 (Object[] elementData) |
| 有序性 | 按插入順序維護 |
| 重複性 | 允許重複元素 |
| Null 支持 | 允許 null 元素 |
| 隨機訪問 | 實現 RandomAccess 接口,get/set O(1) |
| 線程安全 | 非線程安全 |
| 擴容機制 | 1.5 倍 (newCapacity = oldCapacity + (oldCapacity >> 1)) |
核心構造方法
// 默認構造:初始容量 10 (實際延遲到第一次 add 時分配)
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 {
elementData = EMPTY_ELEMENTDATA;
}
}
核心方法源碼解析
add(E e) - 尾部添加
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 確保容量
elementData[size++] = e; // 賦值並 size++
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity); // 默認 10
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 修改次數,用於 fail-fast
if (minCapacity - elementData.length > 0)
grow(minCapacity); // 擴容
}
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);
}
add(int index, E element) - 指定位置插入
public void add(int index, E element) {
rangeCheckForAdd(index); // 校驗索引 0 <= index <= size
ensureCapacityInternal(size + 1);
// 核心:數組拷貝,騰出位置
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
get(int index) - 隨機訪問
public E get(int index) {
rangeCheck(index); // 校驗 0 <= index < size
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index]; // 直接數組下標訪問 O(1)
}
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; // 清除引用,幫助 GC
return oldValue;
}
set(int index, E element) - 替換元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
擴容機制深度解析
- 觸發條件:size + 1 > elementData.length
- 增長因子:1.5 倍 (oldCapacity + oldCapacity/2)
- 最大容量:Integer.MAX_VALUE - 8 (MAX_ARRAY_SIZE)
- 性能影響:涉及 Arrays.copyOf 內存拷貝,頻繁擴容影響性能
最佳實踐:預估數據量,構造時指定初始容量 new ArrayList<>(estimatedSize)。
線程安全問題與解決方案
問題演示
List<Integer> list = new ArrayList<>();
// 多線程並發 add
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) list.add(j);
}).start();
}
// 可能結果:size < 10000、數據丟失、ArrayIndexOutOfBoundsException
解決方案對比
| 方案 | 代碼 | 性能 | 適用場景 |
| Collections.synchronizedList | Collections.synchronizedList(new ArrayList<>()) | 中 (全鎖) | 低並發、簡單場景 |
| CopyOnWriteArrayList | new CopyOnWriteArrayList<>() | 讀快寫慢 | 讀多寫少、遍歷多 |
| 手動同步 | synchronized(list) { list.add(x); } | 可控 | 復合操作需原子性 |
| 並發容器 (推薦) | 無直接對應,用 CopyOnWriteArrayList | 高 | 高並發讀多寫少 |
ArrayList vs LinkedList 選型指南
| 操作 | ArrayList | LinkedList | 選擇建議 |
| get(index) 隨機訪問 | O(1) ✅ | O(n) ❌ | 頻繁隨機訪問用 ArrayList |
| add/remove 末尾 | O(1) 均攤 ✅ | O(1) ✅ | 均可 |
| add/remove 首部/中間 | O(n) ❌ | O(1) 找到節點後 ✅ | 頻繁首部/中間增刪用 LinkedList |
| 內存佔用 | 較小 (數組) | 較大 (節點對象) | 內存敏感用 ArrayList |
| 遍歷性能 | 極快 (CPU 緩存友好) | 較慢 (指針跳轉) | 大量遍歷用 ArrayList |
常用工具方法
// 子列表視圖 (注意:非獨立副本,修改影響原列表)
List<String> sub = list.subList(1, 3); // [1, 3)
// 轉數組
String[] arr = list.toArray(new String[0]); // 推薦傳入類型數組
// 轉字符串
String str = list.toString(); // [a, b, c]
// 批量操作
list.addAll(otherList);
list.removeAll(otherList);
list.retainAll(otherList); // 保留交集
list.containsAll(otherList); // 是否包含所有
// 清空
list.clear(); // 將所有元素置 null,size=0
避坑指南
| 坑點 | 說明 | 正確做法 |
| foreach 中 remove | 拋 ConcurrentModificationException | 用 Iterator.remove() 或 removeIf() |
| subList 修改影響原列表 | subList 返回視圖 | 需獨立副本用 new ArrayList<>(subList) |
| toArray() 無參返回 Object[] | 強轉失敗 | 用 toArray(new String[0]) 或 toArray(String[]::new) |
| 頻繁擴容影響性能 | 默認初始容量 10 | 預估大小指定初始容量 |
| 多線程直接用 | 非線程安全 | 用 CopyOnWriteArrayList 或同步包裝 |
Java 8+ 新特性應用
// removeIf (Java 8) - 線程安全移除
list.removeIf(s -> s.length() < 3);
// replaceAll (Java 8) - 替換所有元素
list.replaceAll(String::toUpperCase);
// sort (Java 8) - 原地排序
list.sort(Comparator.naturalOrder());
// spliterator + Stream
list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
// forEach
list.forEach(System.out::println);
總結
ArrayList 是 Java 最常用的集合,核心優勢:隨機訪問 O(1)、CPU 緩存友好、API 豐富。掌握:擴容機制(1.5倍)、非線程安全、避免 foreach 中修改、預估容量指定初始大小、多線程用 CopyOnWriteArrayList。源碼閱讀重點關注 grow()、System.arraycopy、modCount fail-fast 機制。