在项目的时候,报了一个错 java.util.ConcurrentModificationException
发现是list的remove方法报错,来总结一下:
例子:
public void test(){
List<String> list=new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.stream().forEach(e->{
if ("bbb".equals(e)){
list.remove(e);
}
});
list.stream().forEach(System.out::println);
}
报错信息:
java.util.ConcurrentModificationException
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1388)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at cn.ziwei.test.test.test(test.java:22)
点进去,发现是这里报错:
public void forEachRemaining(Consumer<? super E> action) { // action:{“aaa","bbb","ccc"}
int i, hi, mc; // hoist accesses and checks from loop
ArrayList<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {
if ((hi = fence) < 0) {
mc = lst.modCount; // modCount是修改次数,这里等于3,因为add了3次
hi = lst.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (; i < hi; ++i) {
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e); //这里回去执行你写的逻辑,也就是if里面的判断
}
// 上面的for执行完之后,就会把bbb删除,这个时候lst.modCount=4,因为remove了一次(也就是说,如果if里面的操作是add,也是会报同样的错误)
// 而mc=3,显然if不成立
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
增强for也是如此,大家可以去看一下。不只是remove会报错,add也会,只要能够使modCount发生变化的,都会报java.util.ConcurrentModificationException
下面是增强for,抛异常的地方
例子:
public void test(){
List<String> list=new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
for(String s:list){
if ("bbb".equals(s)) {
list.add("ddd");
}
}
list.stream().forEach(System.out::println);
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
解决方法:
想到了迭代器,强大的迭代器!!
public void test(){
List<String> list=new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
Iterator<String> it = list.iterator();
while (it.hasNext()){
String next = it.next();
if ("bbb".equals(next)){
it.remove();
}
}
list.stream().forEach(System.out::println);
}
不会报错,正常输出。Why?
因为:我们使用的list.remove()或者add()是使用的arraylist里面的方法。这些方法内部会增加modCount的值,而没有修改mc/expectedModCount的值,比如remove的源码:
public E remove(int index) {
rangeCheck(index);
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;
}
而list的迭代器的remove:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; // 改变expectedmodcount来使其不报错
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
关键:expectedModCount = modCount; // 改变expectedmodcount来使其不报错