首页 > 编程语言 >java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法

java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法

时间:2023-02-15 11:07:07浏览次数:59  
标签:MainExt ConcurrentModificationException list equals remove 报错 MainExt2 移除

 

1.情景展示

  已知json对象MainExt

java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法_数据

  需要把值为空数组的key移除掉,下面是执行代码

java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法_DATAFORMAT_02

  执行结果报错:java.util.ConcurrentModificationException 

java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法_数据_03

2.原因分析

  大致过程就是:

  符合删除条件--》调用remove()方法后,expectedModCount-1-1,而modCount-1,在进行下次遍历,也就是执行next()方法体里,又去调用了checkForComodification()方法,

  检查modCount和expectedModCount两者的值不一致,所以会抛出ConcurrentModificationException异常。

java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法_List_04

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表达式是无法实现对同一对象遍历的同时,并完成移除子元素的。

java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法_DATAFORMAT_05

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);

java8 Lambda表达式遍历并移除元素,报错:ConcurrentModificationException的解决办法_List_06

  

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 作者:​​Marydon​​

标签:MainExt,ConcurrentModificationException,list,equals,remove,报错,MainExt2,移除
From: https://blog.51cto.com/u_15964717/6058738

相关文章