【解决】让我们会一会 ConcurrentModificationException

AWS RDS 介绍 三)创建 RDS 实例 AWS RDS 介绍 共分为 7 个部分, 其中 第 3部分重点介绍,如何创建一个RDS 实例。 三) AWS RDS 试用MySQL http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_GettingStarted.CreatingConnecting.MySQL. 阅读详情

我们在敲代码的过程中,难免会和一些Exception不期而遇,就像不知什么时候就会触碰到的女朋友的小脾气,并且,Exception也正像女朋友一样,你需要花上一些时间去了解对方,才知道自己是怎么“死”的。和ConcurrentModificationException的初相遇,那是在一个秋末夜晚,昏黄的路灯下……好了,不扯了。

现在,我们来会一会而ConcurrentModificationException。问题出现在做一个模拟影院自主购票的控制台小项目的过程中,需要一个方法来将选中的电影加入到购物车列表。具体如下。


	Iterator<MovieInCart> it = chosenList.iterator();				
	if(it.hasNext()==false){			    //购物车列表为空
		chosenList.add(movieInCart);<span style="white-space:pre">	</span>            //直接将该电影加入购物车列表
	}else{
		while(it.hasNext()){			     //判断购物车列表里是否已经有该电影
			<strong>MovieInCart movieExisted = it.next();//该语句报错 ConcurrentModificationException</strong>
			if(movieInCart.equals(movieExisted)){//有的话 仅更改已存在的该电影的购买数量
				movieExisted.setQuantity(movieExisted.getQuantity()+quantity);
			} else{							
				chosenList.add(movieInCart);//没有的话  将该电影加入购物车列表
				
			}         
		}
	}

解决

解决方法,直接在chosenList.add(movieInCart);这一语句后加一个break跳出while循环即可。
		while(it.hasNext()){			     //判断购物车列表里是否已经有该电影
			MovieInCart movieExisted = it.next();
			if(movieInCart.equals(movieExisted)){//有的话 仅更改已存在的该电影的购买数量
				movieExisted.setQuantity(movieExisted.getQuantity()+quantity);
			} else{							
				chosenList.add(movieInCart);//没有的话  将该电影加入购物车列表
				<strong>break;//程序若能执行到此,已将选择的电影添加,无需在进行下一次循环</strong>
			}         
		}
另外用增强的for循环的话,也一样加break;即解决问题,只是代码与用迭代器稍有不同,本文在后面也会讲到。
	for(MovieInCart movieExisted:chosenList){
		if(movieInCart.equals(movieExisted)){//有的话 仅更改已存在的该电影的购买数量
			movieExisted.setQuantity(movieExisted.getQuantity()+quantity);
			break; //有一个满足条件修改后的即退出循环</strong>
		} else{							
			chosenList.add(movieInCart);//没有的话  将该电影加入购物车列表	
			break; //添加后即退出循环	
		}
	}

进一步

ConcurrentModificationException意为并发的修改异常,也就是说ConcurrentModificationException容易在我们对ArrayList对象进行多种操作时出现,尤其是循环中。至此,我们所遇到的问题算是已经解决,读者如果只是想要不再看到这恼人的异常,可以就此结束本文的阅读,仔细看看自己的代码有没有用多次的不同的操作修改AraayList对象。
接下来,让我们深入源代码腹地,看看到底是发生了什么。
报错内容很明确地提供了信息:

从上面的报错信息我们可以看出,异常的始发地点是checkForComodification()方法,我们点击后面的提示找到该方法:
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
该方法在ArrayList类下的Itr类中,而Itr类是Iterator接口的实现类,也就是说,当我们对ArrayList用到迭代器时,调用迭代器的方法实际上是调用了Itr类的方法。从checkForComodification()方法中,我们看出异常出现的原因是 modCount != expectedModCount 。这俩又是什么呢?
讲到这,我们应该着手了解一下ArrayList类下的Itr类的一些属性了。
        int cursor;    // index of next element to return  下一元素的下标
        int lastRet = -1; // index of last element returned; -1 if no such   最后一个元素的下标,没有元素则为-1

        int expectedModCount; //初始为modCount,作用是使迭代器的修改次数与ArrayList的相同。
int modCount; //实际上这是AbstractList中的属性。The number of times this list has been structurally modified. 
//该列表被结构性的修改过的次数。structurally ,我们可以理解为任何改变其属性的操作都是structurally的,如next(), remove(), add(E e)等。
乍看上去,似乎记录修改次数并没有什么作用。实际上,modCount和expectedModCount的作用,就是用来判断是否出现了并发修改异常。因为用迭代器遍历的时候,在单线程中,代码开发者是不允许同时对数组列表进行增删等操作的。
如果没有modCount和expectedModCount,而我们又像本文最开始的程序那样调用了ArrayList的add()方法会怎样呢?
	public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                <strong>cursor = i + 1;</strong>
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
查看add(E e)方法的代码我们可以知道,那会对迭代器的游标cusor进行+1操作,而同时add(E e)方法本质还是调用了add(int index, E e)两个参数的方法:
	public void add(int index, E e) {
            rangeCheckForAdd(index);
            checkForComodification();
            parent.add(parentOffset + index, e);
            this.modCount = parent.modCount;
            this.size++;
        }

该方法又对ArrayList的size进行了+1操作。这样一来,迭代器本来可能在本次循环就遍历完了,但由于size又加了1,it.hasNext返回又是true,导致进入下一次循环。这样如果我们假设没有checkForComodification();来检查,那么就将刚刚添加上的电影又当作了已存在的电影,因此进入if语句,将电影数量加一倍,这个过程调用了一次next(),因此游标cursor+1,到下一次while(it.hasNext())判断cursor = size,循环方才停止。这显然不是我们想要的。
为避免上述这样的操作混乱,所以add()、remove()、next()等众多方法中才有了checkForComodification(),因此,有了ConcurrentModificationException。

至此,犹抱琵琶欲露还羞的ConcurrentModificationException终于完全现出芳容。只要我们注意不要在用迭代器的同时三番两次地“得罪”ArrayList对象,她就不会随便发脾气。
不过即使这样,我们还是可以在修改ArrayList上有较大的自由度,只要我们想,并且,只要我们够巧妙。

还有什么

笔者想,既然我们用迭代器会出现这种情况,那么改用增强的for循环呢?
将while循环修改,我们得到如下的代码:
	for(MovieInCart movieExisted:chosenList){
	<span style="white-space:pre">	</span>if(movieInCart.equals(movieExisted)){//有的话 仅更改已存在的该电影的购买数量
			movieExisted.setQuantity(movieExisted.getQuantity()+quantity);
		} else{							
			chosenList.add(movieInCart);//没有的话  将该电影加入购物车列表			
		}
	}
然而,运行程序再次调试,却出现了和用迭代器一模一样的出此同一位置的异常:

说明,增强的for循环的本质,还是迭代器,只不过是隐含的。所以我们还是要在add()方法后加上break; 。
另外,在再次调试中,笔者还发现,添加重复的电影时出现了问题,蝙蝠侠大战超人的数量,笔者先是输入了12,后输入了1,却出现了下图的情况。

  排查半天,笔者发现,是因为if语句中没有break;,所以程序又进入了下一次循环又执行了一次else语句。所以最终正确的代码应该是:
	for(MovieInCart movieExisted:chosenList){
		if(movieInCart.equals(movieExisted)){//有的话 仅更改已存在的该电影的购买数量
			movieExisted.setQuantity(movieExisted.getQuantity()+quantity);
			break; //有一个满足条件修改后的即退出循环</strong>
		} else{							
			chosenList.add(movieInCart);//没有的话  将该电影加入购物车列表
			break; //添加后即退出循环		
		}
	}

最后,不错,我们与 ConcurrentModificationException的会面还算愉快。
不过,尽管我们最终得到了满意的代码,但是在写程序时,我们还是要尽量避免在遍历动态数组的同时对其进行过多的修改操作。


AUTOSAR AP——SM模块 对AUTOSAR AP中状态管理模块SM,从模块定义、总结等多方面进行了介绍。 阅读详情

相关推荐

【主力资金密码】副图指标升级版使用技术图文案例分解,通达信软件炒股指标公式代码

(1)主力资金线在低位时,对应的副图指标显示柱线是绿色,这个阶段主力资金参与小,行情处于下跌趋势或弱势震荡。当然,如果结合主图指标【翻倍密码系统】操作,主图的两个买卖点以及K线均线变色跟踪功能,更多的技术支撑,多空更加清晰,共振做多或多头更加确定和安全。如上图标记,中档虚线是参考安全操作的一个区间划分,粉色阶段做多或持股为主,为操作增加一道安全保护。如上图,副图指标【主力资金密码】更新叠加操作区间背景,绿色阶段持币或做空,粉色阶段做多或持股。如上图,副图指标,标记的两个区间,绿色区间时做空或持币。

weixin_69760830的博客 2552

高并发读写导致ConcurrentModificationException异常的解决办法

1、使用java.util.concurrent包下面的并发容器      网上搜、查API文档。 2、读写分离,写操作加锁(以synchronized为例,其中synchronized锁住的是括号里的对象) Thread thread = new Thread(new Runnable() { public void run() { for (int i

一杯苦芥的专栏 1914

【ESP32】ESP32连接JY61P并通过WIFI发送给电脑

ESP32S连接JY61P并通过WIFI把姿态数据发送给电脑1.采用Arduino IDE编译器;需要安装ESP32的开发板管理器;2.电脑接受数据是基于python的;

Doudou_Nai的博客 1131

HBase之 HTable线程不安全

点击打开链接

myProgrames的博客 840

手把手教你搭建Howland电流源:从原理图到实测避坑指南(附Python数据分析)

本文详细介绍了Howland电流源的设计与实现,从原理图到实测避坑指南,涵盖电阻匹配、运放选择和PCB布局等关键环节。通过Python数据分析,展示了如何优化电路性能并解决常见问题,适合电子工程师和爱好者参考。

weixin_29291069的博客 632

手把手教你用Ollama & AnythingLLM搭建AI知识库,无需编程,跟着做就行!

本地运行大模型耗资源,需要选择较小的模型作为基础模型。在终端中运行时可能会出现 CUDA 错误,表示显存不足,导致提供的端口失效。中文支持不够完善。\3. 文中提到的技术软件工具有:Ollama、Chatbox、Open WebUI、向量数据库、嵌入模型、本地模型 Gemma、AnythingLLM。

2401_86435672的博客 1万+

List之ConcurrentModificationException异常

一、前言     Java开发工作中,集合类ArrayList应该是使用非常频繁了。在使用过程中,可能会遇到迭代删除的需求场景,此时如果代码书写不当,就会抛出 java.util.ConcurrentModificationException 异常信息。下面对这个异常做点分析,为什么会出现异常,怎样去正确的迭代删除。 二、异常分析   测试代码如下: package com.cfang...

weixin_30323961的博客 595

STM32+EC800M-CN 4G模块数据透传踩坑实录:从AT指令调试到花生壳内网穿透

本文详细介绍了STM32与移远EC800M-CN 4G模块的数据透传实现过程,包括硬件连接、AT指令调试、TCP连接建立以及花生壳内网穿透配置。通过实战案例和代码示例,帮助开发者快速解决4G通信模块在物联网应用中的常见问题,提升数据传输稳定性。

weixin_42715608的博客 320

HBase使用常见异常

无法连接ZooKeeper典型异常: INFO ClientCnxn: Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error) 15/07/12 10:06:33 WARN ClientCnxn: Ses

肖韬的BLOG 3478

V.PhyloMaker实战:从物种列表到维管束植物系统发育树的完整流程

本文详细介绍了使用V.PhyloMaker工具包,从物种列表快速构建维管束植物系统发育树的完整实战流程。内容涵盖R包安装、物种名标准化、核心函数phylo.maker的三种构建方案选择、结果树的可视化与导出,以及与picante包的联动分析,为生态学和生物地理学研究提供了高效解决方案。

weixin_29057237的博客 375

手把手教你用P-MOS管搭建双电源自动切换电路(含AO3401选型指南)

本文提供了一份基于P-MOS管AO3401构建高可靠双电源自动切换电路的实战指南。文章详细对比了继电器、理想二极管与MOS管方案的优劣,深入解析了AO3401的关键参数选型,并手把手指导了从原理图设计、PCB布局到上电调试的全过程,旨在帮助硬件工程师和创客以极低成本实现无缝、高效的电源冗余方案。

moss5的博客 1232

ConcurrentModificationException异常原因和解决方法

首先看一下一个例子举出 ConcurrentModificationException 的出现场景: public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(){ { add(1); add(2); add(3); } }; Iterator&

qq_44613591的博客 9万+

Mysql授权允许远程访问

前言 Mysql授权允许远程登录后,linux中安装的mysql可以与Navicat for Mysql(可视化)等连接,在可视化软件下管理数据库。 注意:不能使用临时密码 设置密码命令: alter user 'root'@'localhost' identified by '这里是你的密码'; 授权允许远程访问 一、授权命令: grant all privileges on *.* to '...

🏆 前端领域优质创作者 3万+

Java ConcurrentModificationException问题解决

Java ConcurrentModificationException解决方案为什么会抛出java.util.ConcurrentModificationException异常(如有错误,欢迎指正)一些相关的方法的代码如下:首先看ArrayList的iterator()方法:从这段代码可以看出返回的是一个指向Itr类型对象的引用,我们接着看Itr()方法:几个成员变量解释如下:当调用list.i...

shexidong0269的博客 2012

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【Java】解决Java报错:ConcurrentModificationException

理解并有效处理对于编写健壮的Java程序至关重要。通过本文提供的解决方案和预防措施,开发者可以有效避免和解决这类异常,提高代码质量和可靠性。希望本文能帮助你更好地理解和处理并发修改问题,从而编写出更加可靠的Java应用程序。

Easonmax的博客 2122

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

为什么会出现ConcurrentModificationException异常,如何解决?

在前面一篇文章中提到,对Vector、ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常。下面我们就来讨论以下这个异常出现的原因以及解决办法。 1、异常出现的原因 先看下面这段代码: public class Test { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList

掘客DIGGKR 2952

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

ConcurrentModificationException出现原因及解决方式

异常分析 相信写过一些Java代码的人都遇到过这个异常,看下列代码 import java.util.List; import java.util.ArrayList; public class Test{ public static void main(String[] args){ List<String> list = new ArrayList<>(); list.add("123"); list.add("456");

冰蛙 2372

ConcurrentModificationException异常原因及解决方法

冲天香阵透长安,满城尽带黄金甲 序 上次在写代码时,使用迭代器遍历一个集合,并在迭代中移除(或下新增)集合中的某项,就会出现ConcurrentModificationException异常。 问题复现 问题代码 public static void main(String[] args) { //定义一个集合 List<String> list = ...

A_Runner的博客 2873

java.util.ConcurrentModificationException 异常原因和解决方法

前言 二十多天的实训结束了,虽然环境emmmm有点坑,好多人都感冒了,我也没能逃过一劫. 不过总体来说还行, 第一次尝试跟学校里不一样的,7个人一起做项目. 不过也因此对于github的使用不再局限于之前的将其作为云服务备份来使用了,更多的还是大家上传代码,合并冲突之类的,还有也学会了git stash的一些个基础用法,嘻嘻. 这个月还没写点东西记录一下. 这里就记录一下之前遇到过的一个,算...

y4ung 23万+

已解决Java中java.util.ConcurrentModificationException异常

已解决Java中java.util.ConcurrentModificationException异常

努力才是唯一的入场券。 3万+

ConcurrentModificationException异常解析和解决方法

一、介绍 在Java开发过程中,使用iterator遍历集合的同时对集合进行修改就会出现java.util.ConcurrentModificationException异常,本文就以ArrayList为例去理解和解决这种异常。 二、出现原因 2.1、抛出异常的代码 /** * ConcurrentModificationException复现方法一 */ pu...

严老板的技术梦想博客 2616

ConcurrentModificationException 异常处理

ConcurrentModificationException 是并发编程中常见的问题,通常是因为集合被多个线程共享,并且在迭代过程中被意外修改。通过上述解决方案,可以有效地避免这类问题的发生。

weixin_45809780的博客 954

Java ConcurrentModificationException异常原因和解决方法

转载请标明原文链接:http://www.cnblogs.com/dolphin0520/p/3933551.html 对Vector、ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常。下面我们就来讨论以下这个异常出现的原因以及解决办法。 以下是本文目录大纲: 一.ConcurrentModificationException异常出现的原因 二.在单线程环境下的解决办法 三.在多线程环境下的解决方法 若有不正之处

u013168615的专栏 6612

ConcurrentModificationException在多线程的解决方法

在并发环境下遍历集合如ArrayList并添加或删除元素,如何避免ConcurrentModificationException

qq_45955154的博客 1100

【Java】已解决java.util.ConcurrentModificationException异常

考虑使用并发集合:如果你的应用程序需要处理多线程并发修改集合的情况,考虑使用java.util.concurrent包下的并发集合,如CopyOnWriteArrayList、ConcurrentHashMap等。要正确地在迭代过程中修改集合,可以使用迭代器的remove方法,或者在单线程环境中使用Java 8引入的removeIf方法(如果集合是Collection的某个实现了该方法的子类,如ArrayList)。多线程并发修改:当一个线程正在迭代集合时,另一个线程尝试修改集合的大小或内容。

屿小夏.的知识博客 2002
上一篇: 【解决】Element type "typename" must be followed by either attribute specifications, ">" or "/>".
下一篇: 【解决】Adobe premiere CC 已停止工作
AndrewJoe
博客等级 码龄11年 13粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值