首页
编程随笔
Java笔记
Html/Css/Js
Android
后端笔记
服务器搭建
BUG收集
Java异常
Android异常
在线工具
Json格式化
编码/解码
Epub在线编辑
登录
发布文章
个人文章
退出登录
首页
技术教程
BUG收集
在线工具
资源下载
登录
发布文章
退出登录
搜索
当前位置:
首页
-
博客
- 正文
关闭
List集合遍历出现java.util.ConcurrentModificationException原因分析和解决
更新时间:2021-03-18 23:51:29
阅读数:749
发布者:落幕
当我们使用迭代器遍历List集合时,往往都需要进行一些添加和删除,此时操作不当就会出现异常。 例如1 删除集合里指定某个元素 ```java List
list = new ArrayList<>(3); list.add("aaa"); list.add("bbb"); list.add("ccc"); Iterator
iterator = list.iterator(); String text; while (iterator.hasNext()) { text = iterator.next(); if ("aaa".equals(text)) { // 遍历到“aaa”时删除 list.remove(text); } } ``` 此例子就会出现异常,异常如下: ```java java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) ...... ``` ####原因分析 根据异常信息我们可以知道是在执行next方法时才产生的异常,查看方法源码如下: ```java 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方法源码如下: ```java final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } ``` 可以发现,List中迭代器调用next方法时,都需要对比modCount和expectedModCount是否相等,如果不相等就抛出异常 modCount是List继承自AbstractList的一个成员变量,表示List的修改次数。 expectedModCount: 迭代器初始赋值List修改次数的预期数值(迭代器初始化时list集合的modCount) 由于上面的代码使用了List上的remove函数,导致List上的modCount次数发生了改变(+1)与expectedModCount不再一致,源码如下(段落) ```java public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { // 调用此处移除数据 fastRemove(index); return true; } } return false; } private void fastRemove(int index) { // modCount已经加1,迭代器并无感知 modCount++; 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 } ``` 从源码可以看出,直接使用List.remove(),会导致迭代器感知不到出现异常。此时我们不能再使用List中自带的remove去对数据进行移除, ####解决方法 通过查看迭代器源码,可以发现。迭代器也有移除方法,而且移除元素后expectedModCount 和 modCount能保持一致: ```java 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(); } } ``` 所以最终移除元素可以改为 ```java List
list = new ArrayList<>(3); list.add("bbb"); list.add("aaa"); list.add("ccc"); Iterator
iterator = list.iterator(); String text; while (iterator.hasNext()) { text = iterator.next(); if ("aaa".equals(text)) { // 使用迭代器移除元素 iterator.remove(); } } ```