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异常。

既然不能使用foreach来实现,那么我们该如何实现呢?
主要有以下3种方法:
  1. 使用Iterator的remove()方法
  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如何一边遍历,一边删除?的更多相关文章

  1. java 集合list遍历时删除元素

    本文探讨集合在遍历时删除其中元素的一些注意事项,代码如下 import java.util.ArrayList; import java.util.Iterator; import java.util ...

  2. 【转】ArrayList循环遍历并删除元素的常见陷阱

    转自:https://my.oschina.net/u/2249714/blog/612753?p=1 在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出b ...

  3. Java HashMap 如何正确遍历并删除元素

    (一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K ...

  4. android ndk通过遍历和删除文件

           在做移动开发过程,难免有些本地文件管理操作.例如,很常见app随着微博.微信要清除缓存功能,此功能是走app文件夹.然后删除所有缓存文件.使用java的File类能够实现本地文件遍历及删 ...

  5. 正确在遍历中删除List元素

    最近在写代码的时候遇到了遍历时删除List元素的问题,在此写一篇博客记录一下. 一般而言,遍历List元素有以下三种方式: 使用普通for循环遍历 使用增强型for循环遍历 使用iterator遍历 ...

  6. Java遍历时删除List、Set、Map中的元素(源码分析)

    在对List.Set.Map执行遍历删除或添加等改变集合个数的操作时,不能使用普通的while.for循环或增强for.会抛出ConcurrentModificationException异常或者没有 ...

  7. js 遍历集合删除元素

    js 遍历集合删除元素 /** * 有效的方式 - 改变下标,控制遍历 */ for (var i = 0; i < arr.length; i++) { if (...) { arr.spli ...

  8. ArrayList循环遍历并删除元素的常见陷阱

    在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出bug.不妨把这个问题当做一道面试题目,我想一定能难道不少的人.今天就给大家说一下在ArrayList循环 ...

  9. 【Java】List遍历时删除元素的正确方式

    当要删除ArrayList里面的某个元素,一不注意就容易出bug.今天就给大家说一下在ArrayList循环遍历并删除元素的问题.首先请看下面的例子: import java.util.ArrayLi ...

  10. Java中ArrayList循环遍历并删除元素的陷阱

    ava中的ArrayList循环遍历并且删除元素时经常不小心掉坑里,昨天又碰到了,感觉有必要单独写篇文章记一下. 先写个测试代码: import java.util.ArrayList; public ...

随机推荐

  1. Linux ns 5. IPC Namespace 详解

    文章目录 1. 简介 2. 源码分析 2.1 copy_ipcs() 2.2 ipcget() 2.3 ipc_check_perms() 2.4 相关系统调用 参考文档: 1. 简介 进程间通讯的机 ...

  2. JVM 核心参数

    JVM 内存相关的几个核心参数 参数部分看我笔记   https://note.youdao.com/s/Ch3awnVu JVM模板 1. ParNew + CMS 版 根据服务调整 -Xmx -X ...

  3. spring定时任务ThreadPoolTaskScheduler使用注意事项之线程池大小

    背景 最近小伙伴解决了一个工单,描述为"手工推送案件无法推,提示token失效",当前工单状态为待关闭,解决方案为"东软接口不稳定造成的,东软的接口恢复正常后,问题解决& ...

  4. 通过大量实战案例分解Netty中是如何解决拆包黏包问题的?

    TCP传输协议是基于数据流传输的,而基于流化的数据是没有界限的,当客户端向服务端发送数据时,可能会把一个完整的数据报文拆分成多个小报文进行发送,也可能将多个报文合并成一个大报文进行发送. 在这样的情况 ...

  5. 解决异常:“The last packet sent successfully to the server was 0 milliseconds ago. ”的办法

    出现异常"The last packet sent successfully to the server was 0 milliseconds ago."的大部分原因是由于数据库回 ...

  6. 生产服务GC调优实践基本流程总结

    Photo by Pixabay from Pexels 本文作者:夜色微光 - 博客园 (cnblogs.com) 前言 对Java虚拟机进行性能调优是一个非常宽泛的话题,在实践上也是非常棘手的过程 ...

  7. git使用大全

    创建四个分支: 查看分支:git branch查看仓库里面所有的分支 git branch -a刷新分支 git remote update origin --prune创建分支:git branch ...

  8. js 鼠标放到图片上放大某一部分效果

    动图效果: 代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  9. Codeforces 576D - Flights for Regular Customers(bitset 优化广义矩阵乘法)

    题面传送门 题意: 有一张 \(n\) 个点 \(m\) 条边的有向图,你初始在 \(1\) 号点,边上有边权 \(c_i\) 表示只有当你经过至少 \(c_i\) 条边的时候你才能经过第 \(i\) ...

  10. 洛谷 P7116 - [NOIP2020] 微信步数(拉格朗日插值)

    洛谷题面传送门 我竟然独立切掉了这道题!incredible! 纪念我逝去的一上午(NOIP 总时长 4.5h,这题做了我整整 4.5h) 首先讲一下现场我想的 80 分的做法,虽然最后挂成了 65 ...