【Java面試題】List如何一邊周遊,一邊删除?
這是最近面試時被問到的1道面試題,本篇部落格對此問題進行總結分享。
-
新手常犯的錯誤
可能很多新手(包括當年的我,哈哈)第一時間想到的寫法是下面這樣的:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("部落格園");
platformList.add("CSDN");
platformList.add("掘金");
for (String platform : 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循環倒序周遊
接下來一一講解。
- 使用Iterator的remove()方法的實作方式如下所示:
List<String> platformList = new ArrayList<>();
platformList.add("部落格園");
platformList.add("CSDN");
platformList.add("掘金");
Iterator<String> iterator = platformList.iterator();
while (iterator.hasNext()) {
String platform = iterator.next();
if (platform.equals("部落格園")) {
iterator.remove();
}
}
System.out.println(platformList);
輸出結果為:
[CSDN, 掘金]
為什麼使用iterator.remove();就可以呢?
讓我們看下它的源碼:
可以看出,每次删除一個元素,都會将modCount的值重新指派給expectedModCount,這樣2個變量就相等了,不會觸發java.util.ConcurrentModificationException異常。
- 使用for循環正序周遊的實作方式如下所示:
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;的用途。
- 使用for循環倒序周遊的實作方式如下所示:
List<String> platformList = new ArrayList<>();
platformList.add("部落格園");
platformList.add("CSDN");
platformList.add("掘金");
for (int i = platformList.size() - 1; i >= 0; i--) {
String item = platformList.get(i);
if (item.equals("掘金")) {
platformList.remove(i);
}
}
System.out.println(platformList);
這種實作方式和使用for循環正序周遊類似,不過不用再修正下标,因為剛開始元素的下标是這樣的:
第1次循環将元素"掘金"删除後,元素的下标變成了下面這樣:
第2次循環時i的值為1,也就是取到了元素”CSDN“,不會導緻跳過元素,是以不需要修正下标。
-
參考
Java集合怎麼一邊删除一邊周遊
java 為什麼周遊的時候不能删除元素
原文位址
https://www.cnblogs.com/zwwhnly/p/12530819.html