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