Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 514|回复: 0

Java 迭代器删除元素ConcurrentModificationException异常。

[复制链接]
  • TA的每日心情
    奋斗
    2025-3-18 14:43
  • 签到天数: 805 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    73万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    731050
    发表于 2021-4-26 21:20:34 | 显示全部楼层 |阅读模式

    Java是不支持容器类在使用迭代器迭代过程中,使用如 list.remove(obj)方法删除元素。否则会抛出ava.util.ConcurrentModificationException异常。应该使用iterator.remove()方法删除当前迭代到的元素。

    这是因为Java集合中有一种叫fail-fast的机制,即如果多个线程对同一个集合的内容进行操作时,则会产生fail-fast事件,即抛出异常。

    比如下面代码

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class Hello {
    
        public static void main(String[] args) {
            List<String> all=new ArrayList<String>();
            all.add("Hello");
            all.add("_");
            all.add("World!!");
            Iterator<String> iterator=all.iterator();
            while(iterator.hasNext())
            {
                String str=iterator.next();
                if("Hello".equals(str))
                {
                    all.remove(str);
    
                }
                else
                {
                    System.out.println( str+" ");
                }
            }
            System.out.println("\n删除\"_\"之后的集合当中的数据为:"+all);
        }
    }
    

    删除元素会抛出下面异常

    这里在迭代过程中改变了原来集合的存储内容,因此出发了fail-fast机制。

    fail-fast机制是这样设置的。以上面代码抛出的异常为例。查看ArrayList.类的源码,可以看到抛出异常的函数为

    这里的modCount文档是这样解释的

    The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
    

    大概意思就是list的结构被修改的次数。结构的修改包括改变list的大小,或者其他可能使迭代结果产生错误的干扰行为。

    expectedModCount注释为:

    The modCount value that the iterator believes that the backing List should have.  If this expectation is violated, the iterator has detected concurrent modification.
    

    因此若是在迭代过程中改变了List的结构,即使用了remove,或者等add方法则会导致expectedModCount的值与modCount的值不同,就会抛出异常。

    但是有一个奇特的现象,如果我们使用List.remove(obj)方法删除倒数第二个元素则不会超出异常。如

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class Hello {
    
        public static void main(String[] args) {
            List<String> all=new ArrayList<String>();
            all.add("Hello");
            all.add("_");
            all.add("World!!");
            Iterator<String> iterator=all.iterator();
            while(iterator.hasNext())
            {
                String str=iterator.next();
                if("_".equals(str))
                {
                    all.remove(str);
    
                }
                else
                {
                    System.out.println( str+" ");
                }
            }
            System.out.println("\n删除\"_\"之后的集合当中的数据为:"+all);
        }
    }
    

    注意这里删除的'_'为集合中的倒数第二个元素。同时注意代码有,若没有删除此元素,则输出此元素,即else里面的内容。结果为

    不是说,不能用List.remove(obj)的方式,只能用iterator.remove()的方式删除,这里为什么没有抛异常。原因是这样。

    看ArrayList的hasNext()方法与next()方法的源码

    size为当前集合的大小,cursor为下个元素的索引,初始化为0

    其中checkForComodification的源码为,两个变量的的解释上面也已经说了。

    我们的迭代器部分代码按下面步骤执行:

    all里面存储了“Hello”,"_","world!"三个元素

    1. iterator.hasNext(),
      1. cursor=0,size=3。cursor!=size,返回true,进入while内
    2. String str=iterator.next(),
      1. checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
      2. cursor=i+1,即cursor=1
      3. return elementData[lastRet=i],str='Hello'
    3. '_'.equals(str)为False,输出Hello
    4. iterator.hasNext(),
      1. cursor=1,size=3。cursor!=size,返回true,进入while内
    5. String str=iterator.next(),
      1. checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
      2. cursor=i+1,即cursor=2
      3. return elementData[lastRet=i],str='_'
    6. '_'.equals(str)为Ture,
      1. all.remove(str)
      2. modCount=1,因为改变了List的结构,因此modCount+1。
    7. iterator.hasNext(),
      1. cursor=2,size=2。cursor!=size,返回False,退出

    可以看到,在这里就已经推出while循环了。不会在执行checkForComodification()方法,判断List结构有没有改变。

    结论:因此只要对倒数第二个元素执行remove()方法,虽然会改变集合结构,但是因为也同时改变了集合size,使size与cursor的大小一样,就会直接循环,不会报异常。

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-5-7 10:03 , Processed in 0.062040 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表