[破除迷信]java.util.ArrayList在foreach迴圈遍歷時可以刪除元素
ArrayList是java開發時非常常用的類,常碰到需要對ArrayList迴圈刪除元素的情況。這時候大家都不會使用foreach迴圈的方式來遍歷List,因為它會拋java.util.ConcurrentModificationException異常。比如下面的程式碼就會拋這個異常:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
for (String item : list) {
if (item.equals("3")) {
System.out.println(item);
list.remove(item);
}
}
System.out.println(list.size());
}
}
那是不是在foreach迴圈時刪除元素一定會拋這個異常呢?答案是否定的。
見這個程式碼:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
for (String item : list) {
if (item.equals("4")) {
System.out.println(item);
list.remove(item);
}
}
System.out.println(list.size());
}
}
這段程式碼和上面的程式碼只是把要刪除的元素的索引換成了4,這個程式碼就不會拋異常。為什麼呢?
接下來先就這個程式碼做幾個實驗,把要刪除的元素的索引號依次從1到5都試一遍,發現,除了刪除4之外,刪除其他元素都會拋異常。接著把list的元素個數增加到7試試,這時候可以發現規律是,只有刪除倒數第二個元素的時候不會丟擲異常,刪除其他元素都會丟擲異常。
好吧,規律知道了,可以從程式碼的角度來揭開謎底了。
首先java的foreach迴圈其實就是根據list物件建立一個Iterator迭代物件,用這個迭代物件來遍歷list,相當於list物件中元素的遍歷託管給了Iterator,你如果要對list進行增刪操作,都必須經過Iterator,否則Iterator遍歷時會亂,所以直接對list進行刪除時,Iterator會丟擲ConcurrentModificationException異常。
其實,每次foreach迭代的時候都有兩步操作:
- iterator.hasNext() //判斷是否有下個元素
- item = iterator.next() //下個元素是什麼,並賦值給上面例子中的item變數
next()方法的程式碼如下:
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
這時候你會發現這個異常是在next方法的checkForComodification中丟擲的,丟擲原因是modCount != expectedModCount
- modCount是指這個list物件從new出來到現在被修改次數,當呼叫List的add或者remove方法的時候,這個modCount都會自動增減;
- expectedModCount是指Iterator現在期望這個list被修改的次數是多少次。
iterator建立的時候modCount被賦值給了expectedModCount,但是呼叫list的add和remove方法的時候不會同時自動增減expectedModCount,這樣就導致兩個count不相等,從而丟擲異常。
如果想讓其不丟擲異常,一個辦法是讓iterator在呼叫hasNext()方法的時候返回false,這樣就不會進到next()方法裡了。這裡cursor是指當前遍歷時下一個元素的索引號。比如刪除倒數第二個元素的時候,cursor指向最後一個元素的,而此時刪掉了倒數第二個元素後,cursor和size()正好相等了,所以hasNext()返回false,遍歷結束,這樣就成功的刪除了倒數第二個元素了。
破除迷信,foreach迴圈遍歷的時候不能刪除元素不是絕對,倒數第二個元素是可以安全刪除的~~(當然以上的思路都是建立在list沒有被多執行緒共享的情況下)
相關文章
- Linux迴圈遍歷所有檔案,刪除指定字尾名檔案Linux
- Map迴圈遍歷
- Python優雅遍歷字典刪除元素的方法Python
- 【JavaScript實用技巧(一)】迴圈遍歷與跳出迴圈遍歷JavaScript
- list增強for迴圈刪除元素報錯
- for迴圈無法刪除陣列所有指定元素陣列
- 在ArrayList的迴圈中刪除元素,會不會出現問題?
- jstl forEach遍歷JS
- Golang for迴圈遍歷小坑Golang
- 在lambda的foreach遍歷中break退出(lambda foreach break)
- for in 迴圈遍歷物件時需要注意的事項物件
- list中for迴圈刪除多個元素為何報錯?
- 迴圈遍歷二叉樹二叉樹
- JS迴圈遍歷方法總結JS
- python 元組,列表 迴圈遍歷Python
- python迴圈刪除漏洞Python
- 陣列常見的遍歷迴圈方法、陣列的迴圈遍歷的效率對比陣列
- mybatis foreach迴圈MyBatis
- Java ArrayList 查詢、刪除指定元素;排序;遍歷;隨機獲取元素等常用操作Java排序隨機
- python for迴圈遍歷位置的查詢Python
- Go的迴圈遍歷使用小坑Go
- JavaScript中迴圈遍歷JSON響應!JavaScriptJSON
- 雙向迴圈連結串列:(建立、插入、遍歷、求長、查詢、刪除、排序、銷燬)待測排序
- 記錄java 在遍歷中刪除元素 以及 mysql5.6版本新增unique失敗JavaMySql
- 在迴圈陣列時使用splice()方法刪除陣列遇到的問題陣列
- JavaScript中的12種迴圈遍歷方法JavaScript
- js 跳出迴圈/結束遍歷的方法JS
- 反直覺正迴圈刪除
- jQuery 元素操作——遍歷元素jQuery
- 為什麼for迴圈可以遍歷list:Python中迭代器與生成器Python
- 為什麼阿里巴巴禁止在 foreach 迴圈裡進行元素的 remove/add 操作阿里REM
- Lambda 表示式遍歷集合時用remove方法刪除list集合中滿足條件的元素問題REM
- JS筆記(2) JS中的迴圈遍歷JS筆記
- vue中使用Checkbox 多選框迴圈遍歷Vue
- 2020-11-18 Vue-07迴圈遍歷Vue
- Java碼農必須掌握的迴圈刪除List元素的正確方法Java
- Qt foreach關鍵字遍歷容器QT
- Mybatis框架:foreach迴圈遍歷欄位(為了解決動態表、動態欄位查詢資料)MyBatis框架
- DOM元素的遍歷