每日一題系列:考察 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());
ConcurrentModificationException// 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
...
}
ArrayList 繼承 AbstractList,維護 transient int modCount 記錄結構性修改次數(add/remove/clear)。SubList 構造時拷貝父列表的 modCount 快照。size()、get()、set() 等)首先檢查 parent.modCount == this.modCount,不一致即拋 ConcurrentModificationException。subList(0,2) 創建視圖,subList.modCount = 3(父列表當前 modCount)。list.add(4) 觸發父列表 modCount++ → 4,底層陣列擴容/賦值。subList.size() 調用 → 檢查 parent.modCount (4) != this.modCount (3) → 拋出 CME。List<Integer> safeCopy = new ArrayList<>(list.subList(0, 2));
// 或 Java 10+
List<Integer> safeCopy = list.subList(0, 2).stream().collect(Collectors.toList());
Collections.unmodifiableList(list.subList(...)) 仍共享 modCount,依然會拋異常;唯有「複製」徹底斷開關聯。| 集合 | subList 實現 | 修改原集合後視圖行為 |
|---|---|---|
| ArrayList | 視圖 + modCount 快照 | 拋 CME (fail-fast) |
| Vector | 視圖 + modCount | 拋 CME (同步但仍 fail-fast) |
| LinkedList | 視圖 + modCount | 拋 CME |
| CopyOnWriteArrayList | 無 subList 方法 | N/A |
subList() 為了性能返回視圖,但引入了「原列表修改 → 視圖失效」的強約束。記住鐵律:獲取視圖後,原列表凍結結構性修改;或立即複製斷開關聯。這道題不僅考語法,更考對集合底層機制的理解深度。