循环遍历删除元素的问题-ConcurrentModificationException
在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出bug。下面就是可能出现的一种删除方式。
ArrayList<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
System.out.println(list);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
if (next==4)
list.remove(next);
}
这样就可能出现异常
[3, 4]
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayListItr.next(ArrayList.java:851)
at Leetcode.LeetCode.main(LeetCode.java:68)
发生异常的主要原因就是
在list调用remove方法的时候
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;
}
其中的fastRemove(index) 方法会修改一个modCount变量,这个变量的作用就是表示集合修改的次数(The number of times this list has been structurally modified.)
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
}
当fastRemove修改了modCount变量时,调用next方法
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
其中checkForComodification方法首先就是判断modCount是不是和expectedModCount相等,不相等则抛出异常。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
其中expectedModCount在调用iterator方法的时候,就已经确定了
int expectedModCount = modCount;
注意一点,当删除元素的时候,进行了–size操作,这样在使用hasNext()方法判断集合还有没有元素的时候,使用的是
public boolean hasNext() {
return cursor != size();
}
因此,删除元素之后,即使后面已经没有元素了,可能size和cursor还是不相等,hasNext()返回的结果还为true。
修改代码
ArrayList<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
System.out.println(list);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
if (next==4)
iterator.remove();
}
System.out.println(list);
输出结果
[3, 4]
[3]
为什么迭代器的删除方式不报异常呢?
主要是因为使用迭代器的remove方法,会把expectedModCount重新赋值。
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();
}
}
上一篇: css清除浮动(css 解决浮动元素引起的高度问题)
下一篇: Java多态机制中的绑定规则