【Java -- 并发修改异常(ConcurrentModificationException)】

概述

在利用迭代器遍历集合的时候,如果尝试对集合结构进行修改,就会抛出并发修改异常,如添加、删除元素,仅进行元素内容的修改不会产生并发修改异常。
示例如下:

public class CoModiDemo {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		list.add("Apple");
		list.add("Peach");
		list.add("Lemon");
		
		Iterator<String> it = list.iterator();
		
		while(it.hasNext()) {
			if(it.next().equals("Lemon")) {
				// 迭代时添加元素
				list.add("Banana");
				// 迭代时删除元素
//				list.remove("Lemon");
			}
		}
		
	}
}

运行上述代码,报异常如下:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
	at java.util.ArrayList$Itr.next(ArrayList.java:861)
	at learning.collection.CoModiDemo.main(CoModiDemo.java:17)

源码分析

从错误报告信息中可以看出,错误来源于 ArrayList 类的内部类 Itr 的 next() 方法所调用的 checkForComodification() 方法。

定位到 checkForComodification():

final void checkForComodification() {
      if (modCount != expectedModCount)
          throw new ConcurrentModificationException();
  }

可以看出,抛出异常的原因是变量 modCount 和 变量 expectedModCount 不相等。

  • modCount 是ArrayList 的成员变量,继承自 AbstractList 类,该变量记录了集合发生结构修改的次数,初始值为零。每当集合调用了 add()、remove() 等方法时,该变量加一,如下:
/***** ArrayList 类的 add() 方法 *****/
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private void ensureExplicitCapacity(int minCapacity) {
    // modCount 变量加一
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

public boolean add(E e) {
     ensureCapacityInternal(size + 1);  // Increments modCount!!
     elementData[size++] = e;
     return true;
}
 
 /**** ArrayList 类的 remove() 方法 ****/
public E remove(int index) {
     rangeCheck(index);
	// modCount 变量加一
     modCount++;
     E oldValue = elementData(index);

     int numMoved = size - index - 1;
     if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
                          numMoved);
     elementData[--size] = null; // clear to let GC do its work

     return oldValue;
}
  • expectedModCount 是 ArrayList 类的内部类 Itr 的成员变量,表示预期修改次数。当调用 iterator() 方法获取迭代器时,会创建 Itr 的对象,并且把 ArrayList 对象的 modCount 值赋给 expectedModCount
int expectedModCount = modCount;

也就是说,创建的迭代器 Itr 对象时,expectedModCount的值就固定了,为创建迭代器的集合的modCount值。本文开篇的案例中,由于在迭代中修改了集合的结构,导致其modCount值增加,不再与 expectedModCount 值相等,所以调用 next() 方法时,next() 方法调用的 checkForComodification() 方法中抛出错误。

如何避免并发修改异常

  1. 删除元素时使用迭代器提供的 remove() 方法
    使用迭代器提供的 remove() 方法,导致 modCount 改变时,会随之更新 expectedModCount,将更改后的modCount值赋值给 expectedModCount,所以不会产生并发修改错误。
public class CoModiDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Peach");
        list.add("Lemon");

        System.out.println(list.size());

        Iterator<String> it = list.iterator();

        while(it.hasNext()) {
            if(it.next().equals("Lemon")) {
                it.remove();
            }
        }

        System.out.println(list.size());
    }
}

运行结果:

3
2
  1. 使用 for 循环
public class CoModiDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Peach");
        list.add("Lemon");

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

        System.out.println(list.size());
    }
}

运行结果:

3
4
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值