天天看點

【Java面試題】List如何一邊周遊,一邊删除?

【Java面試題】List如何一邊周遊,一邊删除?

這是最近面試時被問到的1道面試題,本篇部落格對此問題進行總結分享。

  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循環倒序周遊

接下來一一講解。

  1. 使用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異常。

  1. 使用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;的用途。

  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“,不會導緻跳過元素,是以不需要修正下标。

  1. 參考

    Java集合怎麼一邊删除一邊周遊

java 為什麼周遊的時候不能删除元素

原文位址

https://www.cnblogs.com/zwwhnly/p/12530819.html