java8 Lambda表達式遍歷並移除元素,報錯:ConcurrentModificationException的解決辦法


1.情景展示

  已知json對象MainExt

  需要把值為空數組的key移除掉,下面是執行代碼

  執行結果報錯:java.util.ConcurrentModificationException 

2.原因分析

  大致過程就是:

  符合刪除條件--》調用remove()方法后,expectedModCount-1-1,而modCount-1,在進行下次遍歷,也就是執行next()方法體里,又去調用了checkForComodification()方法,

  檢查modCount和expectedModCount兩者的值不一致,所以會拋出ConcurrentModificationException異常。

3.錯誤示例

  錯誤實現一

Iterator<String> iterator = MainExt.keySet().iterator();
while(iterator.hasNext()) {
    String key = iterator.next();
    if ("[]".equals(MainExt.get(key).toString())) {
        MainExt.remove(key);
    }
}

  起初,使用迭代器發現還是失敗,因為在調用next()方法時,又去調用了remove()方法。

  錯誤實現二

JSONObject MainExt2 = new JSONObject(MainExt);
MainExt2.forEach((k,v) -> {
    if ("[]".equals(v.toString())) {
        MainExt.remove(k);
    }
});

  我們咋一看,已經創建了一個新的對象,並將MainExt的值賦給了MainExt2,但是,通過迭代MainExt2,移除MainExt的子節點,移除失敗,

  這才讓我意識到,MainExt2並沒有和MainExt切斷聯系,換句話說就是:MainExt2對MainExt的引用,並不是真正意義上的深層拷貝,要想成功,數據完成拷貝后必須切斷聯系。

4.正確方式

String str = "{\n" +
        "                \"MedicalType\": \"門診\",\n" +
        "                \"PatientNumber\": \"202009041167\",\n" +
        "                \"MedicalDate\": \"20200904-11:41:31\",\n" +
        "                \"OrgType\": \"綜合醫院\",\n" +
        "                \"MedicalInsuranceType\": [],\n" +
        "                \"MedicalInsuranceID\": [],\n" +
        "                \"Gender\": [],\n" +
        "                \"FundPayAmount\": \"0.00\",\n" +
        "                \"OtherPayAmount\": \"0.00\",\n" +
        "                \"AccountPayAmount\": \"0.00\",\n" +
        "                \"OwnPayAmount\": \"0.00\",\n" +
        "                \"SelfpaymentAmount\": \"0.00\",\n" +
        "                \"SelfpaymentCost\": \"0.00\"\n" +
        "            }";
JSONObject MainExt = JSONObject.parseObject(str);
// 深拷貝,不拷貝直接移除key的話,會報錯:ConcurrentModificationException
JSONObject MainExt2 = (JSONObject) MainExt.clone();
MainExt2.forEach((k,v) -> {
    if ("[]".equals(v.toString())) {
        MainExt.remove(k);
    }
});

  通過深層拷貝,復制一份原數據,並與原來對象切斷聯系,迭代復制后的數據,移除原對象的數據。

  說明:說到底,lambda表達式是無法實現對同一對象遍歷的同時,並完成移除子元素的。 

5.list集合移除節點

List<String> list = new ArrayList<>();
list.add("Marydon");
list.add("18");
list.add("test");

  這里需要注意的是:list集合的大小不能為2,如果是2的話,下面無論是哪一種方式都能正常移除!!!

  錯誤方式一:for循環

for (int i = 0; i < list.size(); i++) {
    if ("Marydon".equals(list.get(i))) {
        list.remove(i);
    }
}
System.out.println(list);

  錯誤方式二:foreach

for (String s : list) {
    if ("Marydon".equals(s)) {
        list.remove(s);
    }
}

  錯誤方式三:通過list移除

Iterator<String> ite = list.iterator();
while (ite.hasNext()) {
    String next = ite.next();
    if (next.equals("Marydon")) {
        list.remove(next);
    }
}

  正確方式一:通過迭代器移除

Iterator<String> ite = list.iterator();
while (ite.hasNext()) {
    if (ite.next().equals("Marydon")) {
        ite.remove();
    }
}

  20200907

  方式二:removeAll()

// 存儲將要移除的元素        
List<String> removeList = new ArrayList<>();
for (String s : list) {
    if (s.equals("Marydon")) {
        removeList.add(s);
    }
}
// 移除
list.removeAll(removeList);
System.out.println(list);

  20200908

  方式三:ArrayList.clone()

// 復制list
List<String> newList = (List<String>) ((ArrayList<String>) list).clone();
// 遍歷復制的list
newList.forEach(e -> {
    if ("Marydon".equals(e)) {
        // 移除原來的list子元素
        list.remove(e);
        // 結束本輪循環(可有可無)
        return;
    }
});

System.out.println(list);

  

寫在最后

  哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!

 相關推薦:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM