当前位置: 首页 > article >正文

为什么Foreach循环中为什么不能使用 remove/add操作?

为什么Foreach循环中为什么不能 使用 remove/add操作

foreach 语法糖是 Iterator 实现的

​ 使用 foreach 循环再用其自身的listremove或者add操作会导致抛出 并发修改异常,触发了Java集合检测机制 fail-fast。 这是Iteratorfail-fast快速失效机制,为了避免在并发过程中导致的修改列表的而抛出的异常。

下面来看下发生这个问题的代码

 List<String> list = new ArrayList<>();
 list.add("A");
 list.add("B");
 list.add("C");
 list.add("D");
 for (String s : list) {
 	list.remove(s);
 }
 System.out.println(list);

结果:导致了并发修改异常 ConcurrentModificationException,有个checkForComodification 方法抛出异常,下面会分析

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at top.stormling.javacoresample.module.collection.ArrayListTest.main(ArrayListTest.java:15)

我们先来看下解糖后的代码

 List<String> list = new ArrayList<>();
 list.add("A");
 list.add("B");
 list.add("C");
 list.add("D");
 Iterator<String> iterator = list.iterator();
 do {
 	if (!iterator.hasNext())
 		break;
    String next = (String) iterator.next();//这段代码抛出的checkForComodification 方法
 	if (next.equals("A"))
 		list.remove(next); //注意这段代码
 } while (true);
 	System.out.println(list);

我们注重看下 list.remove(next) 这个源码,

private void fastRemove(Object[] es, int i) {
    modCount++; //注意这段代码
    final int newSize;
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}

ArrayList#remove方法中使用fastRemove 这个方法,从这段代码可以得知使用集合自身修改的操作(如removeadd 方法),会导致修改次数modCount增加。

前面我们看到抛出的异常中有个checkForComodification抛出,这个是 iterator.next() 这里抛出的,也就是意味着当集合中使用集合本身remove() 修改集合后,下一次的 iterator 迭代器进行next()方法是抛出的异常。我们来看下这里面的源码

注意:这里抛出的异常是在ArrayList 中的内部类 ItrIterator的实例)

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方法,这个方法在 Itr

final void checkForComodification() {
    if (ArrayList.this.modCount != this.expectedModCount) { //这段代码
        throw new ConcurrentModificationException();
    }
}

看到这里就会明白,如果我们使用集合本身去执行remove/add操作,或导致 modeCountexpectedModCount 不相等

  • modCount:是ArrayList 成员变量。表示实际修改了几次
  • exceptedModCount:是ArrayList 内部类 Itr 的成员变量。在ArrayList.iterator 方法时被初始化,只有通过迭代器对集合进行操作才会对其值进行修改
  • ItrIterator一个实现,使用ArrayList.iterator 方法可以获取迭代器就是 Itr 类的实例

总结

​ 使用增强for循环,其实就是java使用的语法糖,其实实现的原理是借助Iterator进行元素的遍历。

​ 如果在遍历的过程中,并通过Iterator,而是通过集合类自身的方法对集合进行添加/删除操作,在Iterator 进行下一次的遍历时,经检查发现有一次集合的修改操作并未通过自身进行,则可能发生了并发,而被其他线程执行,这就抛出了异常。


http://www.kler.cn/a/515972.html

相关文章:

  • 网络(三) 协议
  • DC-DC稳压电源——实战(基于Ti5450芯片)基础知识篇(1)
  • Linux权限管理:从用户切换到文件权限
  • 【MYSQL】mysql 常用命令
  • java基础学习——jdbc基础知识详细介绍
  • JS-Web API -day06
  • Vue2 项目二次封装Axios
  • MYSQL学习笔记(五):单行函数(字符串、数学、日期时间、条件判断、信息、加密、进制转换函数)讲解
  • 如何选择TVS二极管?
  • Vue - toRefs() 和 toRef() 的使用
  • 准备知识——波纹度和粗糙度区别与联系
  • 【力扣】1312. 让字符串成为回文串的最少插入次数
  • 法律与认知战争:新时代的战略博弈
  • 8.2 从看图识字到智能解读:GPT-4 with Vision 开启多模态 AI 新纪元
  • Ubuntu下载zenodo文件Ubuntu download zenodo
  • springboot基于微信小程序的手机银行系统
  • 如何区分AI智能体、自动化工作流和PRA?
  • 《Openlayers零基础教程》第十八课:Canvas绘制圆—绘制两个圆
  • 【Trunk接口配置】
  • 【React】 react路由