for循环下操作被循环列表是可能存在坑的,首先我们列出四种循环一个列表并删除一个元素的实现代码
我们执行的结果是,第一种直接报错,第二种没有报错但是存在隐藏坑,第三种和第四种都是可行的方案。
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
import com.alibaba.fastjson.JSONObject;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
/**
* for循环测试
* @author humorchen
* @date 2023/1/10 18:41
*/
public class ForTest {
@Test
public void test1() {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
for (String s : list) {
if ("b".equals(s)) {
list.remove(s);
}
}
System.out.println(JSONObject.toJSONString(list));
}
@Test
public void test2() {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if ("b".equals(s)) {
list.remove(i);
}
}
System.out.println(JSONObject.toJSONString(list));
}
@Test
public void test3() {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if ("b".equals(next)) {
iterator.remove();
}
}
System.out.println(JSONObject.toJSONString(list));
}
@Test
public void test4() {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
String needRemove = null;
for (String s : list) {
if ("b".equals(s)) {
needRemove = s;
break;
}
}
list.remove(needRemove);
System.out.println(JSONObject.toJSONString(list));
}
}
我们可以看到执行test1的结果是报错了
既然有报错那我们一定要搞清楚来龙去脉,我们可以看到报错时的运行栈指向了ArrayList中iterator的next方法,for循环在调用迭代器获取下一个元素时,next方法中第一行就是做了操作数检查checkForComodification();
那我们就来看看这个方法怎么搞的,我们可以看到代码如下
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
他检查了这个arraylist当前的modCount和expectedModCount,也就是当前的值和期望值是否相同,如果不同就抛出ConcurrentModificationException这个异常。
那我们继续追踪看这个期望值是什么时候设置上的,从我截图可以看到,在for循环:这种方式时,其实是去调用了iterator方法获取这个列表的迭代器来遍历,而这个迭代器又是ArrayList自己继承迭代器重写了一些方法,因此对ArrayList循环时要操作的话必须利用迭代器来操作删除,否则报错。
那有的同学就会说了,我可以用下标取删除不会报错啊,可是你要想到当你删除一个元素的时候,集合长度就变化了,那你后续下标再删除就对不上号了~而业务中删除是可能删除任何一个元素,删除任意多个的,即使没有我们也需要让我们写的代码鲁棒性强,避免后面修改的人不知道这些魔法操作而踩坑!