如何避免ConcurrentModificationException

ConcurrentModificationException出现原因:

1.在Java集合类中,当我们在使用iterator遍历集合对象时,当集合被改变且在使用迭代器遍历集合的时候,迭代器的next()方法将抛出ConcurrentModificationException异常。
2.如果subList()生成一个新的List对象,那么当修改原始List对象的结构时,则将抛出ConcurrentModificationException

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ConcurrentModificationExceptionExample {

	public static void main(String args[]) {
		List<String> myList = new ArrayList<String>();

		myList.add("1");
		myList.add("2");
		myList.add("3");
		myList.add("4");
		myList.add("5");

		Iterator<String> it = myList.iterator();
		while (it.hasNext()) {
			String value = it.next();
			System.out.println("List Value:" + value);
			if (value.equals("3")) {
			//由于集合被改变,所以接下来的it.next()会抛出异常
				myList.remove(value);
			}

		}

		Map<String, String> myMap = new HashMap<String, String>();
		myMap.put("1", "1");
		myMap.put("2", "2");
		myMap.put("3", "3");

		Iterator<String> it1 = myMap.keySet().iterator();
		while (it1.hasNext()) {
			String key = it1.next();
			System.out.println("Map Value:" + myMap.get(key));
			if (key.equals("2")) {
			//下面一条语句,由于我们正在更新myMap中的现有键值,因此其大小没有更改,所以没有获取ConcurrentModificationException
			//通过查看源码,HashMap的next方法是调用nextNode().key;获得的,而nextNode()判断修改的标志的是modCount != expectedModCount,即数量不变就为意味着没有修改
			//modCount 的定义:The number of times this HashMap has been structurally modified
				myMap.put("1", "4");
				//如果放开注释,接下来的 it1.next()就会抛出异常
				// myMap.put("4", "4");
			}
		}

	}
}

结果:

List Value:1
List Value:2
List Value:3
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList I t r . c h e c k F o r C o m o d i f i c a t i o n ( A r r a y L i s t . j a v a : 901 ) a t j a v a . u t i l . A r r a y L i s t Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList Itr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.next(ArrayList.java:851)
at ConcurrentModificationExceptionExample.main(ConcurrentModificationExceptionExample.java:20)

如何避免这个异常呢?

1.在单线程下,所以iterator的remove()方法从原集合中删除一个元素

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ConcurrentModificationExceptionExample {

	public static void main(String args[]) {
		List<String> myList = new ArrayList<String>();

		myList.add("1");
		myList.add("2");
		myList.add("3");
		myList.add("4");
		myList.add("5");

		Iterator<String> it = myList.iterator();
		while (it.hasNext()) {
			String value = it.next();
			System.out.println("List Value:" + value);
			if (value.equals("3")) {
				it.remove();
			}

		}
		System.out.println("List Value:" + myList);
		Map<String, String> myMap = new HashMap<String, String>();
		myMap.put("1", "1");
		myMap.put("2", "2");
		myMap.put("3", "3");

		Iterator<String> it1 = myMap.keySet().iterator();
		while (it1.hasNext()) {
			String key = it1.next();
			System.out.println("Map Value:" + myMap.get(key));
			if (key.equals("2")) {
				myMap.put("1", "4");
				// myMap.put("4", "4");
				it1.remove();
			}
		}
		System.out.println("myMap:" + myMap);

	}
}

执行结果:
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Value:[1, 2, 4, 5]
Map Value:1
Map Value:2
Map Value:3
myMap:{1=4, 3=3}

在多线程环境中避免ConcurrentModificationException

1.您可以将列表转换为数组,然后在数组上进行迭代。这种方法适用于中小型列表,但是如果列表很大,则对性能的影响很大。
2.如果使用的是JDK1.5或更高版本,则可以使用ConcurrentHashMap和CopyOnWriteArrayList类。建议使用此方法来避免并发修改异常。



import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class AvoidConcurrentModificationException {

	public static void main(String[] args) {

		List<String> myList = new CopyOnWriteArrayList<String>();

		myList.add("1");
		myList.add("2");
		myList.add("3");
		myList.add("4");
		myList.add("5");

		Iterator<String> it = myList.iterator();
		while (it.hasNext()) {
			String value = it.next();
			System.out.println("List Value:" + value);
			if (value.equals("3")) {
				myList.remove("4");
				myList.add("6");
				myList.add("7");
				myList.remove("3");
			}
		}
		System.out.println("List Size:" + myList);

		Map<String, String> myMap = new ConcurrentHashMap<String, String>();
		myMap.put("1", "1");
		myMap.put("2", "2");
		myMap.put("3", "3");

		Iterator<String> it1 = myMap.keySet().iterator();
		while (it1.hasNext()) {
			String key = it1.next();
			System.out.println("Map Value:" + myMap.get(key));
			if (key.equals("1")) {
				myMap.remove("3");
				myMap.put("4", "4");
				myMap.put("5", "5");
			}
		}

		System.out.println("Map Size:" + myMap);
	}

}


结果:
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Size:[1, 2, 5, 6, 7]
Map Value:1
Map Value:2
Map Value:4
Map Value:5
Map Size:{1=1, 2=2, 4=4, 5=5}

为什么CopyOnWriteArrayList使用iterator时修改原数据就不会报错呢?

   public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
    }

CopyOnWriteArrayList内部静态类
  static final class COWIterator<E> implements ListIterator<E> {
        /** Snapshot of the array */
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;
//传入的是对象,所以原数据修改并不影响它
        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }

        public boolean hasNext() {
            return cursor < snapshot.length;
        }

        public boolean hasPrevious() {
            return cursor > 0;
        }
        ..........

如果subList()生成一个新的List对象,那么当修改原始List对象的结构时,则将抛出ConcurrentModificationException


import java.util.ArrayList;
import java.util.List;

public class ConcurrentModificationExceptionWithArrayListSubList {

	public static void main(String[] args) {

		List<String> names = new ArrayList<>();
		names.add("Java");
		names.add("PHP");
		names.add("SQL");
		names.add("Angular 2");

		List<String> first2Names = names.subList(0, 2);

		System.out.println(names + " , " + first2Names);

//		names.set(1, "JavaScript");
		// names.add("Oracle");
		// check the output below. :)
		System.out.println(names);
		System.out.println(first2Names);

		// Let's modify the list size and get ConcurrentModificationException
		// names.add("NodeJS");
		System.out.println(names); // this line throws exception
		first2Names.set(1, "RabbitMQ");
//		System.out.println(first2Names);
		System.out.println(names);
		System.out.println(first2Names);

		names.add(1, "RabbitMQ2");
		//下一行出现异常
		first2Names.set(1, "RabbitMQ3");
	}

}

结果:
[Java, PHP, SQL, Angular 2] , [Java, PHP]
[Java, PHP, SQL, Angular 2]
[Java, PHP]
[Java, PHP, SQL, Angular 2]
[Java, RabbitMQ, SQL, Angular 2]
[Java, RabbitMQ]
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList S u b L i s t . c h e c k F o r C o m o d i f i c a t i o n ( A r r a y L i s t . j a v a : 1231 ) a t j a v a . u t i l . A r r a y L i s t SubList.checkForComodification(ArrayList.java:1231) at java.util.ArrayList SubList.checkForComodification(ArrayList.java:1231)atjava.util.ArrayListSubList.set(ArrayList.java:1027)
at ConcurrentModificationExceptionWithArrayListSubList.main(ConcurrentModificationExceptionWithArrayListSubList.java:34)

参考文章
CopyOnWriteArrayList

java.util.ConcurrentModificationException

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值