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

Java ArrayList 核心源碼解析與最佳實踐指南

     編輯:Java 相關知識

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.synchronizedListCollections.synchronizedList(new ArrayList<>())中 (全鎖)低並發、簡單場景
CopyOnWriteArrayListnew CopyOnWriteArrayList<>()讀快寫慢讀多寫少、遍歷多
手動同步synchronized(list) { list.add(x); }可控復合操作需原子性
並發容器 (推薦)無直接對應,用 CopyOnWriteArrayList高高並發讀多寫少

ArrayList vs LinkedList 選型指南

操作ArrayListLinkedList選擇建議
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 機制。

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