ArrayList strAl = new ArrayList<>();
strAl.add("a"); strAl.add("b"); strAl.add("c"); strAl.add("d"); // [a,b,c,d]
ArrayList indexes = new ArrayList<>();
indexes.add(0); indexes.add(2); // 要刪除索引 0 和 2
for (int i = 0; i < indexes.size(); i++) {
strAl.remove(indexes.get(i)); // 陷阱在這裡!
}
System.out.println(strAl.size()); // 輸出多少?
輸出:3(預期 2)
最終列表:[b, c, d] → 實際只刪除了 a,c 倖免。
| 步驟 | 操作 | 列表變化 | 索引映射 |
|---|---|---|---|
| 初始 | - | [a, b, c, d] | 0:a, 1:b, 2:c, 3:d |
| i=0 | remove(0) | [b, c, d] | 原 1→新 0, 原 2→新 1, 原 3→新 2 |
| i=1 | remove(2) | [b, c] | 刪除的是「新索引 2」即原 d,而非原 c! |
核心原因:ArrayList.remove(int index) 會將後續元素前移填補空位,導致原索引失效。正向遍歷時,後續要刪除的索引未同步更新,刪錯元素。
for (int i = indexes.size() - 1; i >= 0; i--) {
strAl.remove(indexes.get(i));
}
// 先刪大索引,不影響小索引位置
List toRemove = new ArrayList<>();
for (Integer idx : indexes) {
if (idx >= 0 && idx < strAl.size()) toRemove.add(strAl.get(idx));
}
strAl.removeAll(toRemove);
Iterator it = strAl.iterator();
while (it.hasNext()) {
String s = it.next();
if (需刪除條件) it.remove(); // 安全刪除當前元素
}
LinkedList.remove(int index) 同樣會改變後續節點的索引位置,**同樣存在此陷阱**。逆向遍歷同樣有效。
removeAll/Iterator。