概述
ConcurrentModificationException 可以直接从字面理解:同时更改异常。也就是说,在对某一数据执行某一操作的时候,同时更改了数据,造成错误。
举个栗子
在遍历一个List同时remove其中的某个item
package com.example;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MyClass {
static List<String> mList = new ArrayList<>();
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.initData();
//forEach遍历同时remove某个item
for (String s : mList) {
if ("aaa".equals(s)) {
mList.remove(s);
}
}
myClass.iterator(mList);
//模拟forEach遍历同时remove某个item
Iterator<String> iterator = mList.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if ("aaa".equals(s)) {
mList.remove(s);
}
}
myClass.iterator(mList);
//iterator遍历同时remove某个item
Iterator<String> iterator1 = mList.iterator();
while (iterator1.hasNext()) {
String s = iterator1.next();
if ("aaa".equals(s)) {
iterator1.remove();
}
}
myClass.iterator(mList);
//for遍历同时remove某个item
for (int i = 0; i < mList.size(); i++) {
String s = mList.get(i);
if ("aaa".equals(s)) {
mList.remove(s);
}
}
myClass.iterator(mList);
}
public void initData() {
mList.add("aaa");
mList.add("bbb");
mList.add("ccc");
mList.add("ddd");
}
public void iterator(List<String> list) {
for (String s : list) {
System.out.println(s+" ");
}
}
}
分别用这四种实现逻辑去遍历(注释其它的三种实现),打印结果依次是:
image.png image.png image.png image.png分析问题
从打印的结果我们可以看到前面两种实现会报ConcurrentModificationException ,其实这两种实现本质是一种,第一种forEach的方式其实是在第二种iterator上包了一层语法糖。第三种实现是iterator的正确实现方式,对比一下第二种,区别只是在mList.remove和iterator.remove,OK,这样就成功的缩小了问题范围。接下来,直接看源码:
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
@SuppressWarnings("unchecked")
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
重点看remove方法,ArrayList.this.remove(lastRet)之后,有一个expectedModCount = modCount。再看next()方法的checkForComodification():
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
问题就出在这里,iterator的remove方法有一个同步计数的逻辑。
总结
在遍历List的时候,如果对List数据有改动,不应用forEach的遍历方式,可以用for或者iterator来替代。
网友评论