首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

ArrayList 源碼深度解析:動態數組擴容、增刪改查與性能優化全指南

     編輯:Java 綜合教學

Java 集合框架基石:ArrayList 核心源碼全景圖

ArrayList 是 List 接口最常用的實現,底層由 動態數組 支撐。本文系統梳理其 字段定義、三種構造器、擴容機制、核心增刪改查方法、序列化處理,並給出演示代碼與性能建議,助你在面試與實戰中游刃有餘。

一、核心字段與設計意圖

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;                // 邏輯大小(元素個數)
  • transient:elementData 不參與默認序列化,ArrayList 自定義 writeObject/readObject 只序列化有效元素,節省空間;
  • 兩個空數組常量:EMPTY_ELEMENTDATA 用於 new ArrayList(0),DEFAULTCAPACITY_EMPTY_ELEMENTDATA 用於無參構造,首次 add 時才擴容至 10,實現「延遲分配」。

二、三種構造器語義差異

構造器初始容量適用場景
ArrayList()0(延遲至首次 add 擴容為 10)元素數量未知、較小
ArrayList(int initialCapacity)指定值(>0 分配數組,=0 用 EMPTY,<0 拋異常)能預估大致數據量,避免多次擴容拷貝
ArrayList(Collection<? extends E> c)集合大小集合轉列表、合併數據

三、擴容機制:1.5 倍的科學依據

public void ensureCapacity(int minCapacity) {
    int oldCapacity = elementData.length;
    if (minCapacity > oldCapacity) {
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍
        if (newCapacity < minCapacity) newCapacity = minCapacity;
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}
  • 為什麼 1.5 倍? 2 倍會導致內存碎片難以復用(JVM 堆中連續空間分配),1.5 倍在時間(少擴容)與空間(浪費 ≤33%)間取得平衡;
  • add(E e)、addAll() 等寫入方法均先調用 ensureCapacityInternal(size+1) 檢查擴容。

四、核心增刪改查源碼剖析

4.1 新增操作

  • 尾部添加 add(E e):elementData[size++] = e,攤還 O(1);
  • 指定位置插入 add(int index, E element):System.arraycopy(elementData, index, elementData, index+1, size-index) 向右移動,O(n);
  • 批量追加 addAll(Collection c):轉數組後 System.arraycopy 一次性拷貝;
  • 批量插入 addAll(int index, Collection c):先向右移動騰空間,再拷貝新數據。

4.2 刪除操作

  • 按下標刪除 remove(int index):System.arraycopy(elementData, index+1, elementData, index, size-index-1) 向左移動,最後一位置 null 幫助 GC;
  • 按對象刪除 remove(Object o):遍歷查找首個匹配項調用 fastRemove(index);
  • 範圍刪除 removeRange(from, to):System.arraycopy 一次性前移,尾部置 null;
  • removeAll() 繼承自 AbstractCollection,迭代器調用 remove(),效率較低。

4.3 查找與修改

  • get(int index):RangeCheck(index) 後直接 elementData[index],**隨機訪問 O(1)** 是 ArrayList 最大優勢;
  • set(int index, E element):覆蓋指定位置,返回舊值。

五、序列化與 trimToSize()

  • 自定義 writeObject 只寫入 size 個有效元素,readObject 恢復時按實際大小分配數組;
  • trimToSize():elementData = Arrays.copyOf(elementData, size),釋放多餘預留空間,適合「構建完成後不再變動」的場景。

六、線程安全與性能建議

  1. 非線程安全:多線程寫入需外部同步,或用 Collections.synchronizedList(new ArrayList<>())、CopyOnWriteArrayList(讀多寫少);
  2. 預估容量:new ArrayList<>(estimatedSize) 減少擴容拷貝;
  3. 批量操作優先:addAll 優於循環 add;
  4. 頻繁中間插刪:改用 LinkedList;
  5. 並發遍歷:加鎖或拷貝快照 new ArrayList<>(list) 避免 ConcurrentModificationException。

七、總結

ArrayList 以 動態數組 + 1.5倍擴容 + System.arraycopy 批量移動 為核心,提供了極快的隨機訪問與尾部添加,是「讀多寫少、順序訪問、可預估規模」場景的首選。掌握其擴容邏輯、移動數組的 O(n) 代價、transient 序列化設計,能讓你在容量規劃、性能調優、避坑併發修改上做出最優決策。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved