Exception in thread "main" java.util.ConcurrentModificationException 并发修改异常引发的思考!

1 foreach循环删除元素

  ①list遍历删除元素时会报错,比如下面删除字符串"aa",也有遍历不报错的例子,看下面的例子

public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
array.add("aa");
for (String str : array) {
if("aa".equals(str)){
array.remove(str);
}
} System.out.println(array.size()); }
} console: java.util.ConcurrentModificationException

  ②下面删除字符串"aa"不会报错

public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
for (String str : array) {
if("aa".equals(str)){
array.remove(str);
}
} System.out.println(array.size()); }
} console : 2

提出问题:为什么上面都是遍历删除,第二个确没有报错呢?

结论:其实原因很简单,因为第二个例子没有走iterator的next方法,删除了字符串"aa"之后,执行hasNext方法返回false直接退出遍历了,hasNext中就是判断cursor != size;此时的cursor是2,而size正好也是2,所以退出了遍历。

而第一个例子删除字符串"aa"之后,cursor=2,size=3,所以hasNext方法返回的true,会执行next方法。

注:只要遍历中remove了,expectedModCount和modCount就不相等了。

注2:cursor 就类似游标,这里表示第几次,size就是元素个数

同理:iterator的形式遍历同foreach。

2 上面都是通过foreach的方式进行的,如果用普通for循环会怎么样

public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
array.add("aa");
for(int i = 0;i < array.size();i++){
if("aa".equals(array.get(i))){
array.remove(i);
}
}
System.out.println(array.size()); }
} console: 2

  结论: 普通for循环可以正常删除,他是根据索引进行删除的,所以没有影响。

根据报错信息可以看到是进入checkForComodification()方法的时候报错了,也就是说modCount != expectedModCount。具体的原因,是在于foreach方式遍历元素的时候,是生成iterator,然后使用iterator遍历。在生成iterator的时候,

会保存一个expectedModCount参数,这个是生成iterator的时候List中修改元素的次数。如果你在遍历过程中删除元素,List中modCount就会变化,如果这个modCount和exceptedModCount不一致,就会抛出异常,这个是为了安全的考虑。

看看list的remove源码:

 private void fastRemove(int index) {
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
}

remove操作导致modCount++

private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
  public boolean hasNext() {
  return cursor != size;
  }
  public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}   final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
} 

 总结:  1 foreach遍历,iterator遍历都不能在遍历的过程中使用list.remove或list.add操作,会报并发修改异常。

    2 iterator遍历过程中如果需要删除可以使用iterator提供的remove()方法。

    3 遍历根据元素索引删除是可行的。

以上属于个人心得,不对之处还望大佬指出,如果对你有帮助会更加激励我。  

 

最新文章

  1. 初学者--bootstrap(六)组件中的字体图标----在路上(9)
  2. NPOI操作EXCEL(三)——反射机制进行excel表格数据的解析
  3. [转]Struts2理解--动态方法和method属性及通配符_默认Action
  4. 转载ali面
  5. linux 时间同步
  6. HDU 5155 Harry And Magic Box --DP
  7. 微信小程序开发常见问题分析
  8. 颜色追踪块CamShift---33
  9. Java架构师之路:JAVA程序员必看的15本书
  10. setTimeout、clearTimeout、setInterval,clearInterval ——小小计时器
  11. 深入剖析PE文件
  12. centos7安装nagios步骤
  13. [国嵌攻略][148][MTD系统架构]
  14. Android Navigation使用
  15. video自动填充满父级元素
  16. 几个api看看
  17. HDU 4821 String(2013长春现场赛I题)
  18. centos 6无法上外网
  19. Swift学习笔记 - URL编码encode与解码decode
  20. Spring boot 集成hessian - LocalDateTime序列化和反序列化

热门文章

  1. 《C++ Primer Plus》第2章 开始学习C++ 学习笔记
  2. CentOS7 minimal下MySQL安装
  3. poj_1836 动态规划
  4. 160408、SpringMVC整合Shiro
  5. 阿里云OSS分片上传DEMO
  6. 多线程入门-第五章-线程的调度与控制之yield
  7. 【转】浏览器Request Header和Response Header的内容
  8. libtorch 哪些函数比较常用?
  9. json中load和loads区别
  10. 又一次认识java(四) — 组合、聚合与继承的爱恨情仇