并发修改异常问题
- 遍历集合的同时又存在增删集合元素的行为时可能出现业务异常,这种现象被称之为并发修改异常问题。
假设有个需求:
- 现在购物车中存储了如下商品:JAVA入门,宁夏枸杞,黑枸杞,人字拖,特级枸杞,枸杞子。现在用户不想买枸杞了,选择了批量删除。
分析:
- 后台用ArrayList集合表示购物车,存储这些商品名。
- 遍历集合中的每个数据,只要这个数据包含了“枸杞“则删除它。
- 输出集合看是否已经成功删除了全部枸杞数据了。
按照步骤的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ArrayList<String> list = new ArrayList<>();
list.add("JAVA入门");
list.add("宁夏枸杞");
list.add("黑枸杞");
list.add("人字拖");
list.add("特级枸杞");
list.add("枸杞子");
System.out.println(list);
for (int i = 0; i < list.size(); i++) {
String name = list.get(i);
if (name.contains("枸杞")){
list.remove(name);
}
}
System.out.println(list);
这么做代码看似没问题,实则得到的结果却是:

没有删除干净,实际是因为删除的时候,索引发生了变动。
解决方案如下:
1 | ArrayList<String> list = new ArrayList<>(); |
1 | for (int i = 0; i < list.size(); i++) { |
或者可以倒着索引,即:
1 | for (int i = list.size() - 1; i >= 0; i--) { //这一步倒着来 |
以上两种方案都有一个前提,即集合支持索引,如果不支持索引呢,则需要更换方法
如:
- 用迭代器的方法操作
1
2
3
4
5
6
7
8
9Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String name = iterator.next();
if (name.contains("枸杞")){
// list.remove(name);// 这一步这么写会抛异常
iterator.remove(); // 要用迭代器自己的删除方法
}
}
System.out.println(list);
注意:这个问题用增强for或者lambda都无法解决,这两只适合用作遍历
得出结论:
- 如果集合支持索引,可以使用for循环遍历,每删除数据后做i–;或者可以倒着遍历
- 可以使用迭代器遍历,并用迭代器提供的删除方法删除数据。
- 增强for循环/Lambda遍历均不能解决并发修改异常问题,因此它们只适合做数据的遍历,不适合同时做增删操作。
会特此记录这个BUG源自实习的时候做的第一个需求,大概率就是因为这个BUG,直至离职的时候都未能解决,顿悟的时候已经无法运行校验自己的代码了。