异常产生
当我们在遍历List或者Map的时候,可能会产生java.util.ConcurrentModificationException
异常:
public class ListTest {
@Test
public void test(){
List<String> list = new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
for (String s : list) {
if (s.equals("lisi")) {
list.remove(s);
}
}
}
}
产生原因
在进行for each遍历时,会调用 iterator()
方法,从而产生一个Itr内部类:
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor;
int lastRet = -1;
int expectedModCount = modCount;//expectedModCount为预期修改值,初始化等于modCount(AbstractList类中的一个成员变量)
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification(); //判断expectedModCount和modCount是否相等
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;//保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount) //当modCount 与 expectedModCount 不相等的时候 会抛出ConcurrentModificationException异常
throw new ConcurrentModificationException();
}
}
当我们执行添加、删除操作的时候会调用List中的add 、remove方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
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
}
因此,在遍历下一个数的时候,会检查modCount 与 expectedModCount是否相等,不相等的话就会抛出 ConcurrentModificationException异常。
modCount 含义
/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
*
* <p>This field is used by the iterator and list iterator implementation
* returned by the {@code iterator} and {@code listIterator} methods.
* If the value of this field changes unexpectedly, the iterator (or list
* iterator) will throw a {@code ConcurrentModificationException} in
* response to the {@code next}, {@code remove}, {@code previous},
* {@code set} or {@code add} operations. This provides
* <i>fail-fast</i> behavior, rather than non-deterministic behavior in
* the face of concurrent modification during iteration.
*
* <p><b>Use of this field by subclasses is optional.</b> If a subclass
* wishes to provide fail-fast iterators (and list iterators), then it
* merely has to increment this field in its {@code add(int, E)} and
* {@code remove(int)} methods (and any other methods that it overrides
* that result in structural modifications to the list). A single call to
* {@code add(int, E)} or {@code remove(int)} must add no more than
* one to this field, or the iterators (and list iterators) will throw
* bogus {@code ConcurrentModificationExceptions}. If an implementation
* does not wish to provide fail-fast iterators, this field may be
* ignored.
*/
大概意思:在使用迭代器遍历的时候,用来检查列表中的元素是否发生结构性变化(列表元素数量发生改变)了,主要在多线程环境下需要使用,防止一个线程正在迭代遍历,另一个线程修改了这个列表的结构。
所以 在单线程情况下,我们遍历ArrayList 并且需要操作的时候 ,可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护modCount和expectedModCount值的一致性。
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
String str = iter.next();
if( str.equals("wangwu") ){
iter.remove();
}
}