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

Java 集合源碼深度剖析:ArrayList 與 LinkedList 底層結構、擴容、迭代器與效能選型

     編輯:Java 相關知識

一、ArrayList 源碼核心解析 (JDK 8/11/17 通用)

1. 核心欄位

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;

2. 三種建構子

  • ArrayList():elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA(延遲初始化)
  • ArrayList(int initialCapacity):預分配陣列,避免擴容開銷
  • ArrayList(Collection<? extends E> c):c.toArray() 複製元素

3. 擴容機制:grow() 核心邏輯

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(舊版 2.0),平衡記憶體與複製開銷
  • MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8(留給 VM header)
  • 每次擴容伴隨 Arrays.copyOf 完整陣列複製

4. 迭代器與 Fail-Fast 機制

private class Itr implements Iterator<E> {
    int cursor, lastRet = -1;
    int expectedModCount = modCount; // 快照

    public E next() {
        checkForComodification();
        // ...
    }
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}
  • modCount:結構性修改次數(add/remove/clear)
  • 迭代器建立時快照 expectedModCount,遍歷中檢測不一致拋出 CME
  • 單執行緒也會觸發:迭代中直接呼叫 list.remove() 而非 itr.remove()

5. ListItr:雙向迭代器 + set/add

  • 繼承 Itr,實作 ListIterator
  • 新增 previous()、hasPrevious()、set()、add()

6. 多執行緒替代方案:CopyOnWriteArrayList

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally { lock.unlock(); }
}
  • 寫時複製:讀無鎖、寫加鎖複製新陣列
  • 適合「讀多寫少」、可容忍短暫資料不一致場景(快取、監聽器列表)
  • 記憶體開銷大、寫入延遲高

二、LinkedList 源碼核心解析

1. 節點結構:雙向鏈表

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) { ... }
}

2. 核心欄位

transient int size = 0;
transient Node<E> first;
transient Node<E> last;

3. 四大核心操作

操作方法時間複雜度關鍵邏輯
頭插linkFirst()O(1)新節點 prev=null, next=first; first.prev=新節點
尾插linkLast()O(1)新節點 prev=last, next=null; last.next=新節點
指定位置插入linkBefore()O(n) 定位 + O(1) 插入找到 succ,新節點 prev=succ.prev, next=succ
刪除節點unlink()O(n) 定位 + O(1) 刪除斷開 prev/next 連結,協助 GC

4. 隨機存取優化:node(int index)


Node<E> node(int index) {
    if (index < (size >> 1)) { // 前半段從頭遍歷
        Node<E> x = first;
        for (int i = 0; i < index; i++) x = x.next;
        return x;
    } else { // 後半段從尾遍歷
        Node<E> x = last;
        for (int i = size - 1; i > index; i--) x = x.prev;
        return x;
    }
}
  • 雙向鏈表特性:索引 < size/2 從 first 走,否則從 last 走
  • 實作 List 介面隨機存取,但效能仍為 O(n)

5. Deque 介面實作:雙端佇列

  • push()/pop() = addFirst()/removeFirst()(棧)
  • offer()/poll() = addLast()/removeFirst()(佇列)
  • peekFirst()/peekLast() 查看不移除

三、ArrayList vs LinkedList vs Vector 效能選型指南

操作ArrayListLinkedListVector
隨機存取 get/set⭐⭐⭐ O(1)⭐ O(n)⭐⭐⭐ O(1)
尾部新增 add⭐⭐⭐ 攤平 O(1)⭐⭐⭐ O(1)⭐⭐ 同步開銷
頭部/中間新增⭐ O(n) 陣列位移⭐⭐ O(n) 定位 + O(1) 插入⭐ 同步開銷
刪除指定位置⭐ O(n) 陣列位移⭐⭐ O(n) 定位 + O(1) 刪除⭐ 同步開銷
記憶體開銷低(陣列)高(Node 物件 + 參考)低
執行緒安全❌❌✅ synchronized
擴容策略1.5 倍不適用2 倍

選型決策樹

  1. 需執行緒安全 → Collections.synchronizedList() 或 CopyOnWriteArrayList(Vector 已過時)
  2. 高頻隨機存取、尾部新增為主 → ArrayList
  3. 高頻頭部/中間插入刪除、需棧/佇列特性 → LinkedList
  4. 不確定 → ArrayList(預設首選,JVM 優化最好、記憶體局部性佳)

四、避坑指南

  • foreach 迴圈中 remove → 必用 Iterator.remove() 或 Java 8 removeIf()
  • 大量資料預知規模 → 建構子指定 initialCapacity 避免多次擴容
  • 子列表視圖 → subList() 返回視圖,修改視圖影響原列表,序列化會失敗
  • toArray() 陷阱 → toArray(new String[0]) 比 new String[size] 更快(JVM 優化)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved