這是最近面試時被問到的1道面試題,本篇文章對此問題進行總結分享,
文章來源于微信公眾號丨程式員吳師兄
1. 新手常犯的錯誤
可能很多新手(包括當年的我,哈哈)第一時間想到的寫法是下面這樣的:
publicstaticvoidmain(String[]args){List<String>platformList=newArrayList<>();platformList.add("博客園");platformList.add("CSDN");platformList.add("掘金");for(Stringplatform:platformList){if(platform.equals("博客園")){platformList.remove(platform);}}System.out.println(platformList);}
然后滿懷信心地去運行,結果竟然拋java.util.ConcurrentModificationException例外了,翻譯成中文就是:并發修改例外,
是不是很懵,心想這是為什么呢?
讓我們首先看下上面這段代碼生成的位元組碼,如下所示:
由此可以看出,foreach回圈在實際執行時,其實使用的是Iterator,使用的核心方法是hasnext()和next(),
然后再來看下ArrayList類的Iterator是如何實作的呢?
可以看出,呼叫next()方法獲取下一個元素時,第一行代碼就是呼叫了checkForComodification();,而該方法的核心邏輯就是比較modCount和expectedModCount這2個變數的值,
在上面的例子中,剛開始modCount和expectedModCount的值都為3,所以第1次獲取元素"博客園"是沒問題的,但是當執行完下面這行代碼時:
platformList.remove(platform);
modCount的值就被修改成了4,
所以在第2次獲取元素時,modCount和expectedModCount的值就不相等了,所以拋出了java.util.ConcurrentModificationException例外,
既然不能使用foreach來實作,那么我們該如何實作呢?
主要有以下3種方法:
使用Iterator的remove()方法
使用for回圈正序遍歷
使用for回圈倒序遍歷
接下來一一講解,
2. 使用Iterator的remove()方法
使用Iterator的remove()方法的實作方式如下所示:
publicstaticvoidmain(String[]args){List<String>platformList=newArrayList<>();platformList.add("博客園");platformList.add("CSDN");platformList.add("掘金");Iterator<String>iterator=platformList.iterator();while(iterator.hasNext()){Stringplatform=iterator.next();if(platform.equals("博客園")){iterator.remove();}}System.out.println(platformList);}
輸出結果為:
[CSDN,掘金]
為什么使用iterator.remove();就可以呢?讓我們看下它的原始碼:
可以看出,每次洗掉一個元素,都會將modCount的值重新賦值給expectedModCount,這樣2個變數就相等了,不會觸發java.util.ConcurrentModificationException例外,更多面試題,歡迎關注公眾號 Java面試題精選
3. 使用for回圈正序遍歷
使用for回圈正序遍歷的實作方式如下所示:
public static void main(String[] args) { List<String> platformList = new ArrayList<>(); platformList.add("博客園"); platformList.add("CSDN"); platformList.add("掘金"); for (int i = 0; i < platformList.size(); i++) { String item = platformList.get(i); if (item.equals("博客園")) { platformList.remove(i); i = i - 1; } } System.out.println(platformList); }
這種實作方式比較好理解,就是通過陣列的下標來洗掉,不過有個注意事項就是洗掉元素后,要修正下下標的值:
i=i-1;
為什么要修正下標的值呢?因為剛開始元素的下標是這樣的:
第1次回圈將元素"博客園"洗掉后,元素的下標變成了下面這樣:
第2次回圈時i的值為1,也就是取到了元素”掘金“,這樣就導致元素"CSDN"被跳過檢查了,所以洗掉完元素后,我們要修正下下標,這也是上面代碼中i = i – 1;的用途,更多面試問題可以關注微信訂閱號碼將筆記回復面試獲取
4. 使用for回圈倒序遍歷
使用for回圈倒序遍歷的實作方式如下所示:
publicstaticvoidmain(String[]args){List<String>platformList=newArrayList<>();platformList.add("博客園");platformList.add("CSDN");platformList.add("掘金");for(inti=platformList.size()-1;i>=0;i--){Stringitem=platformList.get(i);if(item.equals("掘金")){platformList.remove(i);}}System.out.println(platformList);}
這種實作方式和使用for回圈正序遍歷類似,不過不用再修正下標,因為剛開始元素的下標是這樣的:
第1次回圈將元素"掘金"洗掉后,元素的下標變成了下面這樣:
第2次回圈時i的值為1,也就是取到了元素”CSDN“,不會導致跳過元素,所以不需要修正下標,
好了,這篇文章就到這里,感謝大家的閱讀,喜歡的話給個三連吧~
作為一名編程學習者,如果你想更好地提升你的編程能力,好好學習C/C++編程知識以及資料結構,以后努力成為高薪演算法/軟體開發工程師的話!
C語言C++編程學習交流圈子,QQ群464501141【點擊進入】微信公眾號:C語言編程學習基地
分享(原始碼、專案實戰視頻、專案筆記,基礎入門教程)
歡迎轉行和學習編程的伙伴,利用更多的資料學習成長比自己琢磨更快哦!
編程學習書籍:

編程學習視頻:

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/266259.html
標籤:其他
