迭代器元素刪除

異常內容

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接口的引用,不需要關注數據的實際組織方式,可以使用統一的方式進行訪問。對代碼設計很有幫助的哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章