List如何一边遍历,一边删除?
1.新手常犯的错误
可能很多新手(包括当年的我,哈哈)第一时间想到的写法是下面这样的:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
for (String platform : platformList) {
if (platform.equals("博客园")) {
platformList.remove(platform);
}
}
System.out.println(platformList);
}
然后满怀信心的去运行,结果竟然抛java.util.ConcurrentModificationException异常了,翻译成中文就是:并发修改异常。

是不是很懵,心想这是为什么呢?
让我们首先看下上面这段代码生成的字节码,如下所示:

由此可以看出,foreach循环在实际执行时,其实使用的是Iterator,使用的核心方法是hasnext()和next()。
然后再来看下ArrayList类的Iterator是如何实现的呢?

可以看出,调用next()方法获取下一个元素时,第一行代码就是调用了checkForComodification();,而该方法的核心逻辑就是比较modCount和expectedModCount这2个变量的值。
在上面的例子中,刚开始modCount和expectedModCount的值都为3,所以第1次获取元素"博客园"是没问题的,但是当执行完下面这行代码时:
platformList.remove(platform);
modCount的值就被修改成了4。

所以在第2次获取元素时,modCount和expectedModCount的值就不相等了,所以抛出了java.util.ConcurrentModificationException异常。

主要有以下3种方法:
2. 使用for循环正序遍历
3. 使用for循环倒序遍历
2. 使用Iterator的remove()方法
使用Iterator的remove()方法的实现方式如下所示:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
Iterator<String> iterator = platformList.iterator();
while (iterator.hasNext()) {
String platform = iterator.next();
if (platform.equals("博客园")) {
iterator.remove();
}
}
System.out.println(platformList);
}
输出结果为:[CSDN, 掘金]
为什么使用iterator.remove();就可以呢?
让我们看下它的源码:

可以看出,每次删除一个元素,都会将modCount的值重新赋值给expectedModCount,这样2个变量就相等了,不会触发java.util.ConcurrentModificationException异常。
3. 使用for循环正序遍历
使用for循环正序遍历的实现方式如下所示:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
for (int i = 0; i < platformList.size(); i++) {
String item = platformList.get(i);
if (item.equals("博客园")) {
platformList.remove(i);
i = i - 1;
}
}
System.out.println(platformList);
}
输出结果为:[CSDN, 掘金]
这种实现方式比较好理解,就是通过数组的下标来删除,不过有个注意事项就是删除元素后,要修正下下标的值:i = i - 1;
为什么要修正下标的值呢?
因为刚开始元素的下标是这样的:

第1次循环将元素"博客园"删除后,元素的下标变成了下面这样:

第2次循环时i的值为1,也就是取到了元素”CSDN“,这样就导致第二个博客园元素被跳过检查了,所以删除完元素后,我们要修正下下标,这也是上面代码中i = i - 1;的用途。
4.使用for循环倒序遍历
使用for循环倒序遍历的实现方式如下所示:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("CSDN");
platformList.add("掘金");
for (int i = platformList.size() - 1; i >= 0; i--) {
String item = platformList.get(i);
if (item.equals("CSDN")) {
platformList.remove(i);
}
}
System.out.println(platformList);
}
输出结果为:[博客园, 掘金]
这种实现方式和使用for循环正序遍历类似,不过不用再修正下标,因为刚开始元素的下标是这样的:

第2次循环将元素"CSDN"删除后,元素的下标变成了下面这样:

第3次循环时i的值为1,也就是取到了元素”CSDN“,不会导致跳过元素,所以不需要修正下标。
原文链接:https://blog.csdn.net/zwwhnly/article/details/104987143
List如何一边遍历,一边删除?的更多相关文章
- java 集合list遍历时删除元素
本文探讨集合在遍历时删除其中元素的一些注意事项,代码如下 import java.util.ArrayList; import java.util.Iterator; import java.util ...
- 【转】ArrayList循环遍历并删除元素的常见陷阱
转自:https://my.oschina.net/u/2249714/blog/612753?p=1 在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出b ...
- Java HashMap 如何正确遍历并删除元素
(一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K ...
- android ndk通过遍历和删除文件
在做移动开发过程,难免有些本地文件管理操作.例如,很常见app随着微博.微信要清除缓存功能,此功能是走app文件夹.然后删除所有缓存文件.使用java的File类能够实现本地文件遍历及删 ...
- 正确在遍历中删除List元素
最近在写代码的时候遇到了遍历时删除List元素的问题,在此写一篇博客记录一下. 一般而言,遍历List元素有以下三种方式: 使用普通for循环遍历 使用增强型for循环遍历 使用iterator遍历 ...
- Java遍历时删除List、Set、Map中的元素(源码分析)
在对List.Set.Map执行遍历删除或添加等改变集合个数的操作时,不能使用普通的while.for循环或增强for.会抛出ConcurrentModificationException异常或者没有 ...
- js 遍历集合删除元素
js 遍历集合删除元素 /** * 有效的方式 - 改变下标,控制遍历 */ for (var i = 0; i < arr.length; i++) { if (...) { arr.spli ...
- ArrayList循环遍历并删除元素的常见陷阱
在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出bug.不妨把这个问题当做一道面试题目,我想一定能难道不少的人.今天就给大家说一下在ArrayList循环 ...
- 【Java】List遍历时删除元素的正确方式
当要删除ArrayList里面的某个元素,一不注意就容易出bug.今天就给大家说一下在ArrayList循环遍历并删除元素的问题.首先请看下面的例子: import java.util.ArrayLi ...
- Java中ArrayList循环遍历并删除元素的陷阱
ava中的ArrayList循环遍历并且删除元素时经常不小心掉坑里,昨天又碰到了,感觉有必要单独写篇文章记一下. 先写个测试代码: import java.util.ArrayList; public ...
随机推荐
- css盒模型简介
如何了解盒模型 盒模型简介:盒模型是css布局的基石,它规定了网页元素如何显示以及元素间相互关系.css定义所有的元素都可以拥有像盒子一样的外形和平面空间. 盒模型的组成:内容区.补白/填充.边框.边 ...
- Vue.js教程 2.体验Vue
Vue.js教程 2.体验Vue <!DOCTYPE html> <html lang="en"> <head> <meta charse ...
- Docker多机网络
前言 前面的文章主要聚焦于单机网络上,对于生产环境而言,单机环境不满足高可用的特点,所以是不具备上生产的条件,因此在开始Docker Swarm篇的时候我们先来聊聊多机网络之间Docker的通信如何做 ...
- RabbitMQ (五):死信队列
什么是TTL RabbitMQ的TTL全称为Time-To-Live,表示的是消息的有效期.消息如果在队列中一直没有被消费并且存在时间超过了TTL,消息就会变成了"死信" (Dea ...
- webRTC中语音降噪模块ANS细节详解(四)
上篇(webRTC中语音降噪模块ANS细节详解(三))讲了噪声的初始估计方法以及怎么算先验SNR和后验SNR. 本篇开始讲基于带噪语音和特征的语音和噪声的概率计算方法和噪声估计更新以及基于维纳滤波的降 ...
- Nginx的try_files指令使用实例
Nginx的配置语法灵活,可控制度非常高.在0.7以后的版本中加入了一个try_files指令,配合命名location,可以部分替代原本常用的rewrite配置方式,提高解析效率. try_file ...
- [loj2265]最长上升子序列
以下内容参考2019年集训队论文<浅谈杨氏矩阵在信息学竞赛中的应用> 1.前置知识 杨表 标准杨表:一张网格图,满足以下条件-- 1.设其有$m$行.第$i$行有$a_{i}$个格子(格子 ...
- lilypond和弦及其转位的表示
在lilypond,如果要打和弦的话,有所谓的chordmode,命令就是\chordmode {} 要使用chordmode需要一些基本的和弦命名的知识,最好先补一下乐理 实际上lilypond的官 ...
- 洛谷 P4709 - 信息传递(置换+dp)
题面传送门 一道挺有意思的题罢-- 首先看到这种与置换乘法相关的题,首先把这些置换拆成一个个置换环,假设输入的置换有 \(m\) 个置换环,大小分别为 \(s_1,s_2,\cdots,s_m\),显 ...
- 数据仓库分层中的ODS、DWD、DWS
1.数据仓库DW 1.1简介 Data warehouse(可简写为DW或者DWH)数据仓库,是在数据库已经大量存在的情况下,为了进一步挖掘数据资源.为了决策需要而产生的,它是一整套包括了etl.调度 ...