概述
ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错
文章目录
- ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错
- 一、说明
- 二、`ArrayList`迭代器中的`remove()`方法的作用
- 三、并发情况下,在调用`next()`方法之后调用`remove()`方法也会报错
- 四、如何应对遍历时改变集合导致的未决行为?
一、说明
在开发中,经常使用ArrayList的迭代器进行遍历元素,例如:
ArrayList<String> list = new ArrayList<>(Arrays.asList("aa", "bb", "cc"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
思考:在上面的迭代中,如果调用remove()
方法,会不会报错?
作者已经通过阅读源代码(java version “1.8.0_281”),先给出结论:
- 非并发情况下,在调用
next()
方法之前调用remove()
方法会报错; - 非并发情况下,在调用
next()
方法之后调用remove()
方法不会报错; - 并发情况下,在调用
next()
方法之后调用remove()
方法也会报错;
下面进行分析。
类名.this :内部(可以是匿名内部类)类调用外部类的对象时使用,即在内部类中使用时:外部类对象是外部类名.this,内部类对象则是this。
二、ArrayList
迭代器中的remove()
方法的作用
ArrayList迭代器中的remove()
方法,可以用于删除迭代过程中的上一次迭代的元素。因此,在非并发情况下,在调用next()
方法之后调用remove()
方法不会报错。
它只能删除游标指向的前一个元素,而且一个 next() 函数之后,只能跟着最多一个 remove() 操作,多次调用 remove() 操作会报错
private void listDemo() {
ArrayList<String> list = new ArrayList<>(Arrays.asList("aa", "bb", "cc"));
Iterator<String> iterator = list.iterator();
System.out.println("第一次遍历:......");
while (iterator.hasNext()) {
// iterator.remove(); // 这里调用 remove方法会报错
System.out.println(iterator.next());
iterator.remove(); // 这里调用remove方法不会报错
System.out.println(list);
}
}
上main打印的内容如下:
第一次遍历:......
aa
[bb, cc]
bb
[cc]
cc
[]
三、并发情况下,在调用next()
方法之后调用remove()
方法也会报错
下面是ArrayList的源代码,其中modCount
和 expectedModCount
初始值都是0。
ArrayList在使用迭代器遍历的过程中,如果调用remove() 方法,会删除上一次迭代的元素(索引为lastRet)。每次删除,modCount会加1,expectedModCount会在删除完成之后,等于modCount的值。这个时候,并发就会导致modCount 和 expectedModCount的值不一样,导致判断checkForComodification()
的时候报错。
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();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
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;
}
四、如何应对遍历时改变集合导致的未决行为?
有两种比较干脆利索的解决方案:一种是遍历的时候不允许增删元素,另一种是增删元素之后让遍历报错。
第一种思路比较难实现。
java采用第二种思路:增删元素之后,让遍历报错。
选择 fail-fast 解决方式,抛出运行时异常,结束掉程序,让程序员尽快修复这个因为不正确使用迭代器而产生的 bug 。
在 ArrayList 中定义一个成员变量 modCount,记录集合被修改的次数,集合每调用一次增加或删除元素的函数,就会给 modCount 加 1。当通过调用集合上的 iterator() 函数来创建迭代器的时候,我们把 modCount 值传递给迭代器的 expectedModCount 成员变量,之后每次调用迭代器上的 hasNext()、next()、currentItem() 函数,我们都会检查集合上的 modCount 是否等于 expectedModCount,也就是看,在创建完迭代器之后,modCount 是否改变过。
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.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;
// ......
}
最后
以上就是健康缘分为你收集整理的ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错的全部内容,希望文章能够帮你解决ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复