在實際開發中,我們經常需要在遍歷 ArrayList 時動態移除符合條件的元素。乍看之下似乎很直觀,但若不當處理極易拋出 IndexOutOfBoundsException。本文將分析錯誤成因,並提供兩種正確且推薦的解決方案。
for (int i = 0, len = list.size(); i < len; ++i) {
if (list.get(i) == XXX) {
list.remove(i); // 危險!
}
}
執行結果會拋出異常:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
...
remove(i) 會導致後續元素前移,索引變小,但迴圈變數 i 仍持續遞增,且 len 未更新。當遍歷到尾部時,實際集合大小已小於原始 len,導致 get(i) 索引越界。
for (int i = 0, len = list.size(); i < len; ++i) {
if (list.get(i) == XXX) {
list.remove(i);
--len; // 關鍵:同步縮減預期長度
}
}
雖然可行,但程式碼較繁瑣,且易因漏改 len 而埋下隱患。
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String e = it.next();
if (e.equals("3")) {
it.remove(); // 安全移除當前元素
}
}
List 內部實作了 Iterator,remove() 由 JDK 保證線程安全與索引一致性。ArrayList 原始碼可發現,許多方法(如 removeAll、retainAll)底層均委託給 Iterator 實作。遍歷集合時需刪除元素,請優先使用 Iterator 的 remove() 方法。若效能極度敏感且集合較小,手動調整索引長度亦可接受,但維護成本較高。切記:絕不可在 for-each 或傳統 for 迴圈中直接呼叫集合的 remove()。