ConcurrentModificationException异常出现的解决方案

问题

ArrayList作为Java程序中最常被使用的集合类型,使用迭代方式遍历该集合,并删除其中的某些特定元素是一个很常见的操作场景。我们直接看来示例代码:

public class TestForeach{

public static void main(String args[]) {
List list = new ArrayList();
list.add("大锤");
list.add("二锤");

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

此代码编译正确,运行时却会抛出如下的错误信息:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at arraylist.AddRemoveListElement.main(AddRemoveListElement.java:17)
解决方案一

我们可以使用 Iterator遍历 代替 for循环 ,从而解决此问题。在 Iterator 遍历的过程中,允许使用 remove()方式去删除底层支撑的集合中的元素。

List list = new ArrayList();
list.add("大锤");
list.add("二锤");
Iterator iter = list.iterator();

while(iter.hasNext()){
String str = iter.next();
if( str.equals("二锤") )
{
iter.remove();
}
}

此方案的背后的逻辑比较复杂,我总结出以下几个要点,希望可以帮助理解:
1. for循环只是Java的语法糖,底层还是会使用 Iterator 方式。
2. ArrayList的实现细节中,有一个记录ArrayList底层数组修改次数的计数器
3. ArrayList的remove方法会修改计数器的值
4. Iterator的remove方法会修正计数器的值
5. Iterator 迭代的过程会检查计数器的值

解决方案二
 

如果不使用 ArrayList, 而是使用 CopyOnWriteArrayList, 那么一样可以解决此错误。CopyOnWriteArrayList 是ArrayList的一个线程安全的变种,它的每次修改自身的操作(add, set, remove)都会在拷贝底层数组,从而获得一个全新的数组。

public static void main(String args[]) {
List list = new CopyOnWriteArrayList();
list.add("大锤");
list.add("二");

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

当然了,CopyOnWriteArrayList 由于每次修改操作都需要拷贝获取新的底层数组,性能上会低于ArrayList。

思维扩展

前面,我们演示了ArrayList的问题和两个解决方案,那么同为JDK中LinkedList和HashSet等其他的Collection类似是否也存在相同的问题呢?我们来看两个实例代码:

public static void main(String args[]) {
Set<String> set = new HashSet();
set.add("大锤");
set.add("二");

for (String s : set) {
if (s.equals("二")) {
set.remove(s);
}
System.out.println(s);
}
}

//输出结果 :二锤大锤 
public static void main(String args[]) {
LinkedList<String> llist = new LinkedList();
llist.add("大锤");
llist.add("二");

for (String s : llist) {
if (s.equals("二")) {
llist.remove(s);
}
System.out.println(s);
}
}

//输出结果: 大锤二锤(s.equals("大锤")时只会输出大锤)

建议大家上机验证,这两个实例代码的运行结果与具体的 JDK 的版本有关。具体而言,需要参考 ArrayList、HashSet 和 LinkedList 这三个类的内部迭代器 Iterator 类的 hasNext() 方法的实现细节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值