在Java ArrayList上循环时避免出现ConcurrentModificationExcep

除了NullPointerException和ClassNotFoundException之外,ConcurrentModificationException是Java开发人员的另一个噩梦。是什么让这个错误棘手的是并发,它总是误导Java程序员,这个异常即将到来,因为多个线程试图在同一时间修改集合。然后开始打猎,他们花了无数个小时找到有并发修改概率的代码。而实际上ConcurrentModficationException也可以来自单线程环境。给你一个例子,只是循环使用for循环的列表,并尝试删除一个元素,你会得到ConcurrentModificatoinExcetpion?为什么?因为你打破了在迭代期间不修改集合的规则。

Java如何知道抛出ConcurrentModificationExeption?它使用一个名为modCount的瞬态变量,它跟踪一个列表在结构上被修改了多少次。结构修改是那些改变列表大小,这可能会影响迭代的进展,并可能会产生不正确的结果。这两个迭代器和的ListIterator使用此字段来检测意外的变化。List的其他结构修改List的方法也使用这个方法,例如add(),remove()。问题:遍历ArrayList并删除选定的元素,但remove()抛出“

线程“main”java.util.ConcurrentModificationException“中的异常。

原因: ConcurrentModfiicationException的真正原因是modCount不一致。当你遍历ArrayList的那么迭代器的next()方法跟踪modCount的的。如果通过添加或删除元素来修改集合,那么modCount将会改变,并且不会与预期的modCount相匹配,因此Iterator将抛出ConcurrentModificationException。

这里是来自hasNext()方法的代码片段,它显示了检查modCount:

public E next() { 
    checkForComodification(); 
    int i = cursor; 
    if (i >= size) 
        throw new NoSuchElementException(); 
    Object[] elementData = ArrayList.this.elementData; 
    if (i >= elementData.length) 
        throw new ConcurrentModificationException(); 
    cursor = i + 1; 
    return (E) elementData[lastRet = i]; 
}

现在,如果你检查这个checkForComodification()方法,你会发现我刚才说的:

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

解决方案:如果在单线程环境中使用Iterator,则使用Iterator,否则使用并发收集类(例如CopyOnWriteArrayList)在循环时删除元素。

解决ArrayList中的ConcurrentModificationException

这里是Java程序来演示一个场景,即使只有一个线程正在修改ArrayList,也会得到ConcurrentModificationException。在这个例子中,我们使用高级的for循环和删除选定的元素来循环ArrayList ,但是因为我们正在使用ArrayList的remove()方法。

import java.util.ArrayList; 
import java.util.Arrays;
import java.util.Iterator; 
import java.util.List; 
 /** * Java Program to demonstrate how to deal with 
 * ConcurrentModificationException. 
 * Unlike the name suggests, this error can come even if only 
 * one thread is modifying the collection e.g. List. 
 * It happens when you modify collection 
 * while iterating over it e.g. adding new element or removing elements. 
 *
 * If you want to remove elements while traversing list then 
 * make sure you use Iterator's remove() method or not ArrayList's remove() 
 * method() to avoid ConcurrentModificationExcetpion. 
 * 
 * @author WINDOWS 8 
 * 
 */

 public class ConcurrentModExceptionDemo{ 
 
	public static void main(String args[]) { 
	
	List<String> listOfPhones = new ArrayList<String>(Arrays.asList( "iPhone 6S", "iPhone 6", "iPhone 5", "Samsung Galaxy 4", "Lumia Nokia"));
	
	System.out.println("list of phones: " + listOfPhones); 
	
	// Iterating and removing objects from list
	// This is wrong way, will throw ConcurrentModificationException 
	for(String phone : listOfPhones){ 
		if(phone.startsWith("iPhone")){ 
			// listOfPhones.remove(phone); // will throw exception 
		} 
	} 
	// The Right way, iterating elements using Iterator's remove() method 
	for(Iterator<String> itr = listOfPhones.iterator(); itr.hasNext();){
		String phone = itr.next(); 
		if(phone.startsWith("iPhone")){ 
		// listOfPhones.remove(phone); 
		// wrong again itr.remove(); 
		// right call 
		} 
	} 
	System.out.println("list after removal: " + listOfPhones); 
	}
}
Output : 
list of phones: [iPhone 6S, iPhone 6, iPhone 5, Samsung Galaxy 4, Lumia Nokia] 
list after removal: [Samsung Galaxy 4, Lumia Nokia]

如果您取消注释第一循环和第二循环中的注释代码,你会得到以下异常:

异常在线程“主要” java.util.ConcurrentModificationException 

在java.util.ArrayList中的$ Itr.checkForComodification(来源不明)

在java.util中。 ArrayList $ Itr.next(未知源)

在dto.ReverseArrayInPlace.main(ReverseArrayInPlace.java:28),

因为我们正在使用ArrayList的remove()方法。在第二个示例中,我们使用了Iterator的remove()方法,这就是为什么我们能够成功地从ArrayList中删除选定的元素而没有ConcurrentModificationException。

以下是关于在Java中循环ArrayList时解决ConcurrentModificationException的重要观点的总结:

这就是如何处理Java中的ConcurrentModificationException。要学习和记住的最大的事情是,即使只有一个线程修改集合,例如在列表中循环时删除元素,也会出现此错误。



关于“ 在Java ArrayList上循环时避免出现ConcurrentModificationExcep 的评论

发表评论