首先准备一个list进行简单的赋值操作。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
// 迭代器 Iterator
Iterator<Integer> itr = list.iterator();
while(itr.hasNext()){
Integer next = itr.next();
if(next.equals(2)){
itr.remove();
}
System.out.println("next = " + next);
}
// foreach 删除元素报错
// for (Integer i : list) {
// if(i == 2){
// list.remove(i);
// }
// System.out.println("i = " + i);
// }
// foreach 删除元素不报错
// for (Integer i : list) {
// if(i == 3){
// list.remove(i);
// }
// System.out.println("i = " + i);
// }
System.out.println("list = " + list);
}
-
首先ArrayList使用的foreach原理是使用的迭代器Iterator, ArrayList中存在一个内部类Itr实现了Iterator接口,然后在进行遍历的时候其实就是使用迭代器进行一系列的操作。
内部类Itr代码:
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 boolean hasNext() { return cursor != size; } @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(); } } @Override @SuppressWarnings("unchecked") 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++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
-
首先通过Itr中的hasNext方法进行判断是否到了最后一个元素
public boolean hasNext() { return cursor != size; }
-
-
然后使用Itr中的next方法获取下一个元素
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]; }
next方法中通过checkForComodification方法校验arrayList中的modCount改动次数和Iterator中的expectedModCount期望改动次数是否相同,如果不同则抛出异常(删除元素后的异常就是在这里抛出的)
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
- 最后使用ArrayList中的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方法进行元素的删除操作
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方法中将modeCount进行了++操作(该操作导致了最终的异常抛出),然后将数组元素进行了copy操作,集合大小size进行了减一操作。
-
删除数据之后进行下一轮的循环,重复上面的三个步骤,第一步比较游标cursor和size依旧不相等,然后在走到第二步next方法中时,首先在checkForComodification方法进行校验的时候,这时由于执行完remove方法之后modCount数值进行++操作,而Itr中的expectedModCount值并未进行改变,所以这里就不相等了,然后抛出了异常ConcurrentModificationException。
-
如果删除的数据为倒数第二个,删除完之后进行下一轮的循环,重复上面的三个步骤,然后走到第一步hasNext方法时,由于remove之后size变成了3,此时游标cursor也变成了3,所以hasNext返回false,直接跳出循环不进行下一个元素的遍历了,故不会抛出对应的异常。
总结
- 首先list在循环的过程中删除推荐使用迭代器Iterator
// 迭代器 Iterator
Iterator itr = list.iterator();
while(itr.hasNext()){
Object next = itr.next();
if(next.equals(2)){
itr.remove();
}
System.out.println("next = " + next);
}
正常进行删除操作
2. 在使用for进行循环的时候,进行删除元素,如果当前删除的元素不是倒数第二个元素则会抛ConcurrentModificationException异常,
3. 如果当前删除的元素为倒数第二个元素,则不会抛出异常,但最后一个元素不会进行遍历到
从截图中的执行结果可以看出,倒数第二个元素3虽然能够正常删除,但是在遍历的时候并没有使用break操作跳出循环,但是最后一个元素4并没有打印出。