我是靠谱客的博主 健康缘分,最近开发中收集的这篇文章主要介绍ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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的源代码,其中modCountexpectedModCount初始值都是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方法会不会报错所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部