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

Java 每日一題:ArrayList.subList 視圖特性與併發修改陷阱

     編輯:Java 相關知識

每日一題系列:考察 ArrayList.subList() 返回視圖的特性,以及對原列表修改後對子列表的影響。這是面試高頻考點,也是生產 Bug 高發區。

題目代碼

List<Integer> list = new ArrayList<>();
list.add(1); list.add(2); list.add(3);

List<Integer> subList = list.subList(0, 2); // [1, 2]

list.add(4); // 原列表新增元素

System.out.println("原列表長度:" + list.size());
System.out.println("子列表長度:" + subList.size());

選項分析

  • A. 原列表長度:4,子列表長度:2
  • B. 原列表長度:4,子列表長度:3
  • C. 原列表長度:4,子列表長度:4
  • D. 拋出異常

正確答案:D —— 拋出 ConcurrentModificationException

深度解析

subList 返回的是「視圖」而非副本

// ArrayList.subList 實現片段
public List<E> subList(int fromIndex, int toIndex) {
    return new SubList(this, 0, fromIndex, toIndex);
}

// 內部類 SubList 關鍵字段
private class SubList extends AbstractList<E> {
    private final AbstractList<E> parent; // 引用原列表
    private final int offset;
    private final int size;
    private int modCount; // 快照:創建視圖時父列表的 modCount
    ...
}

modCount 機制導致 fail-fast

  • ArrayList 繼承 AbstractList,維護 transient int modCount 記錄結構性修改次數(add/remove/clear)。
  • SubList 構造時拷貝父列表的 modCount 快照。
  • 視圖的任何操作(size()、get()、set() 等)首先檢查 parent.modCount == this.modCount,不一致即拋 ConcurrentModificationException。

本題執行流程

  1. subList(0,2) 創建視圖,subList.modCount = 3(父列表當前 modCount)。
  2. list.add(4) 觸發父列表 modCount++ → 4,底層陣列擴容/賦值。
  3. subList.size() 調用 → 檢查 parent.modCount (4) != this.modCount (3) → 拋出 CME。

避坑指南與最佳實踐

  1. 視圖僅供唯讀、短暫使用:獲取子列表後立即遍歷/轉換,不要持有視圖引用跨業務邊界。
  2. 需要獨立副本請顯式複製:
    List<Integer> safeCopy = new ArrayList<>(list.subList(0, 2));
    // 或 Java 10+
    List<Integer> safeCopy = list.subList(0, 2).stream().collect(Collectors.toList());
    
  3. 多線程環境下更危險:即使單線程,只要原列表在視圖存活期內被修改即觸發異常。
  4. 替代方案:Collections.unmodifiableList(list.subList(...)) 仍共享 modCount,依然會拋異常;唯有「複製」徹底斷開關聯。

擴展:其他集合的 subList 行為

集合subList 實現修改原集合後視圖行為
ArrayList視圖 + modCount 快照拋 CME (fail-fast)
Vector視圖 + modCount拋 CME (同步但仍 fail-fast)
LinkedList視圖 + modCount拋 CME
CopyOnWriteArrayList無 subList 方法N/A

總結

subList() 為了性能返回視圖,但引入了「原列表修改 → 視圖失效」的強約束。記住鐵律:獲取視圖後,原列表凍結結構性修改;或立即複製斷開關聯。這道題不僅考語法,更考對集合底層機制的理解深度。

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