迭代器元素删除

异常内容

java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.chen.chenarraylist.ChenTest.testRemove(ChenTest.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Process finished with exit code -1

异常代码

    @Test
    public void testRemove() {
        ArrayList<Integer> integers = new ArrayList<>();
        integers.add(1);
        integers.add(2);
        integers.add(3);

        for (Integer integer : integers) {
//            删除小于2的元素
            if (integer < 2) {
                integers.remove(integer);
            }
        }
    }

异常原因分析

查看异常输出,可以知道发生了java.util.ConcurrentModificationException异常。刚刚遇到的时候有点懵逼。WC,怎么会出现这样的异常。其实只要看一下Iterator的源码就可以解决这个问题。

//该函数的功能是新建一个成员内部类对象Iter
public Iterator<E> iterator() {
        return new Itr();
    }

Itr类的源代码

 private class Itr implements Iterator<E> {
        int cursor;       // 下一个要返回的元素位置
        int lastRet = -1; // 最后一个返回的索引位置,如果没有默认为-1
        int expectedModCount = modCount;//期望修改次数

        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];
        }

其中checkForComodification()的源代码为:

final void checkForComodification() {
//如果在迭代过程中发生修改,直接抛出ConcurrentModificationException异常,这正是我们异常提示所报的异常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

综上,异常分析完毕。那如何解决呢?预知后事如何,且听下回分解。

异常解决思路

既然next()函数会首先检查当前是否发生了结构性变化。那我们直接修改expectedModCount即可。
修改后的代码:

    @Test
    public void testRemoveRight() {
        ArrayList<Integer> integers = new ArrayList<>();
        integers.add(1);
        integers.add(2);
        integers.add(3);

        Iterator<Integer> iterator = integers.iterator();
        while (iterator.hasNext()) {
            // 删除小于2的元素
            if (iterator.next() < 2) {
                iterator.remove();
            }
        }

//        输出删除后的结果
        iterator = integers.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        /**
         * 运行结果:
         * 2
         * 3
         * 成功将1删除
         */
    }

总结

遇到问题不要慌,多看源码,问题慢慢就会解决。By the way、使用迭代器,需要访问容器元素的代码只需要一个Iterator接口的引用,不需要关注数据的实际组织方式,可以使用统一的方式进行访问。对代码设计很有帮助的哦!

发布了22 篇原创文章 · 获赞 4 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/qq_32510597/article/details/104992016