故事
为了在 CSDN 的问答社区获取几个基本,发现写的太多都可以作为一篇博文了,就顺道发了。
为了那几个原力分我容易吗?我容易吗?你还在这看我这博文,你忍心不点赞吗!!!
提问内容
题目
初始化一个 LinkedList 链表,然后采用迭代器向其添加元素
LinkedList<Integer> li = new LinkedList<Integer>();
li.add(0);
for(int i = 1; i < 3; ++i){
int size = li.size();
Iterator<Integer> itr = li.iterator();
for(int j = 0; j < size; ++j){
li.add(itr.next()+1);
}
}
报错
在采用迭代器进行元素添加元素的时候出现了并发报错
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.117 sec <<< FAILURE!
test1(MainTest) Time elapsed: 0.015 sec <<< ERROR!
java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966)
at java.util.LinkedList$ListItr.next(LinkedList.java:888)
at MainTest.test1(MainTest.java:17)
如何解决
将
Iterator<Integer> itr = li.iterator();
...
li.add("xxx");
改用
ListIterator<Integer> itr = li.listIterator();
...
itr.add("xxx");
添加时候完全交给迭代器进行完成
正确代码
LinkedList<Integer> li = new LinkedList<Integer>();
li.add(0);
for(int i = 1; i < 3; ++i){
int size = li.size();
ListIterator<Integer> itr = li.listIterator();
for(int j = 0; j < size; ++j){
itr.add(itr.next()+1);
}
}
li.forEach(System.out::println);
问题分析
1、先看报错
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.117 sec <<< FAILURE!
test1(MainTest) Time elapsed: 0.015 sec <<< ERROR!
java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966)
at java.util.LinkedList$ListItr.next(LinkedList.java:888)
at MainTest.test1(MainTest.java:17)
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:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
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.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
2、找到对我们有用的信息
java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966)
at java.util.LinkedList$ListItr.next(LinkedList.java:888)
at MainTest.test1(MainTest.java:17)
可以看到,报错因为我们执行到 li.add(itr.next()+1);时候报错
为什么报错是因为在 LinkedList 中的子类 ListItr 的 next 方法中 888 行,next 方法又调用了 checkForComodification 方法报错,所以我们去看一下 LinkedList 的源码分别找到 888 和 966 行
分别如下
从图中可以看出来,结果了,因为 modCount 与 expectedModCount 不相等,所以就抛出了异常。那为什么不相等呢?我们看看 modCount 和 expectedModCount 究竟是啥。
modcount 大概的作用呢就是用作统计被修改的次数,也就是当你的整体的结构发生修改,eg:add、remove 的时候他就会 ++ 或者--,从而就会造成正在迭代的数据出现不正确的结果,如果该字段发生改变就会抛出 ConcurrentModificationException 异常
now,你 maybe 大概明白了,因为你在进行 add 的时候使得 modCount 发生了改变。
有意思的事
你或许会发现当你在执行第一次循环(i=1)的时候程序是正常执行的,但是当你执行第二次循环的时候报错了,为什么呢?
第一次循环(i=1)
第一次(i=1)的时候,li.size()的大小是 1,里面只有一个值 0,所以在进行循环
Iterator<Integer> itr = li.iterator();
for(int j = 0; j < size; ++j){
li.add(itr.next()+1);
}
的时候,你只是执行了一次,也就是说:
你在 li.add()这个方法执行了一次就退出了 j 循环,当你调用 itr.next()+1 的时候,他的 modCount 和 expectedModCount 的值是一样的,所以能够正常返回,当你执行结束 li.add 的时候 modCount 值发生了 modCount++ 的操作,但是这个时候你已经退出循环了,无所谓。
第二次循环(i=2)
1)这个时候你需要仔细分析一下:
1:li 中的元素现在有 0、1,
2:li.size()的值是 2,也就是会进行 2 次 J 循环
2)重头戏:
当 j=0 的时候进入了第一次循环这个时候执行 li.add(itr.next()+1)的时候,你说有没有出现异常,答案是没有!
因为这个过程就行第一次循环的那一部分阐述一样,当他第一次执行 itr.next()的时候 modCount 值是相等的,所以能够正常返回数值,而执行到 li.add()的时候 li 发生了 add 操作导致 modCount++,然后这个时候执行完这一行他就继续下一次循环。
当 j = 1 的时候,这个时候他要执行 itr.next()获取值,就会去 LinkedList 中的 next 方法中去,在获取的时候会先执行 checkForComodification 这个方法去校验 modCount 和 expectedModCount 是否相等,如果不相等就说明发生了改变就会抛出 ConcurrentModificationException 异常。
也就是说当你 j=1 的时候执行 itr.next 发现了 modCount 值变化了,所以就抛出了异常。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于