List 集合遍历过程中删除元素避坑指南。

news/2025/4/1 5:21:42/

文章目录

  • 1. 遍历
  • 2. 遍历过程中删除元素
    • 2.1 for 简单循环正向遍历方式
    • 2.2 for 简单循环反向遍历方式
    • 2.3 foreach 方式遍历删除
    • 2.4 Iterator的remove()方法
    • 2.5 <font color = green> removeIf() (推荐)<green>
    • 2.6 Strem 方式


在这里插入图片描述

作为一名后端开发,不管采用什么语言 使用 List 集合的频率都非常高。

对 List 集合的遍历和遍历中操作数据也是家常便饭。

我从我使用的过程中对于此问题的思考与实践形成记录,与大家交流。有不对的地方,恳请大家指正。

1. 遍历

List 集合的遍历有很多种方式,每一种遍历方式只有性能上的差异,不会有异常和暗坑。这里不再赘述。


2. 遍历过程中删除元素

因为 List 集合有多种遍历方式,也就意味着存在多种遍历中删除的方式:

在做各种方式比对前,我们先加入一些初始数据。

 List<String> list = new ArrayList<>();list.add("snow");list.add("nier");list.add("sar");list.add("juaya");list.add("rock");list.add("snow");list.add("nier");

2.1 for 简单循环正向遍历方式

for (int i = 0; i < list.size(); i++) {String name = list.get(i);if(name.equals("snow")){list.remove(i);}if(name.equals("nier")){list.remove(i);}
}System.out.println(list);

上述代码打印结果:

 [nier, sar, juaya, rock, nier]

很明显 这不是正确的结果。
这是 因为:在遍历过程中,删除或者增加元素后,集合长度会因为实时改动而改动,也就是说集合在遍历过程中忘了初心了。

举个栗子:现在对长度为 5 的集合进行遍历,在遍历到 第三个元素的时候删除了ta,那么此时集合长度成了4,那么此时集合就不会遍历到之前下标为 5 的元素了。

知道了原因后,我们可以对症下药,达到我们的预期。修改代码如下:

for (int i = 0; i < list.size(); i++) {String name = list.get(i);if(name.equals("snow")){list.remove(i);//	加补偿机制i--;}if(name.equals("nier")){list.remove(i);//	加补偿机制i--;}
}System.out.println(list);

上述代码执行结果:

[sar, juaya, rock]

如此 加入了补偿机制后虽然达到了预期结果。但是很明显代码不够优雅。So 这种方式不推荐。


2.2 for 简单循环反向遍历方式

反向遍历和正向遍历类似,只不过是反方向的钟而已。

for (int i = list.size() - 1; i >= 0; i--) {String name = list.get(i);if(name.equals("snow")){list.remove(i);}if(name.equals("nier")){list.remove(i);}
}System.out.println(list);

上述代码执行结果:

[sar, juaya, rock]

咦~可以哎。结果正确。那这种方式是不是就绝对可以了呢。 NO!

逆序遍历虽然在这个场景下达到了预期的正确结果,但是因为 这种方式和正序在原理上是相同的。所以在遍历过程中操作元素也会有其劣根性。比如在逆序遍历过程中加入元素,依然会对整个遍历产生影响。 所以这也不是推荐的方式。


2.3 foreach 方式遍历删除

单从遍历来看 这种方式比上述两种方式优雅了不少。但是遍历过程中操作元素能达到预期结果吗?

for (String s : list) {if(s.equals("rock")){list.remove("rock");}
}

执行结果…
执行结果

What???

在这里插入图片描述

原因: 这种循环方式,其生成的字节码其实使用的 Iterator ,使用的核心方法是 `hasnext()` 和 `next()`。 然后再来看下ArrayList类的 Iterator 是如何实现的呢? Iterator 部分源码:

Iterator 部分源码

可以看出:调用next()方法获取下一个元素时,第一行代码就是调用了checkForComodification();

而该方法的核心逻辑就是比较 modCountexpectedModCount 这2个变量的值。
在上面的例子中,刚开始 modCountexpectedModCount 的值都为 n,所以第1次获取元素 “rock” 是没问题的,但是当执行完下面这行代码时:

list.remove("rock");

modCount 的值就被修改了。
remove()
So: 使用 foreach 遍历过程中删除元素是不可行的。


2.4 Iterator的remove()方法

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){String next = iterator.next();if("snow".equals(next)){iterator.remove();}
}

执行结果:

[nier, sar, juaya, rock, nier]

很 nice !

那这种方式怎么就可以删除了呢???

因为:

这种方式每次删除一个元素,都会将modCount 的值重新赋值给 expectedModCount,这样2个变量就相等了,不会触发 java.util.ConcurrentModificationException 异常。

在这里插入图片描述

虽然这种方式符合了业务要求,但是还是不推荐这种方式,因为他的写法依然不够优雅。


2.5 removeIf() (推荐)

从JDK1.8开始,可以使用 removeIf() 方法来代替 Iterator的remove()方法实现一边遍历一边删除。

list.removeIf(s -> s.equals("snow"));System.out.println(list);

打印结果:

[nier, sar, juaya, rock, nier]

其源码如下:
源码


2.6 Strem 方式

list = list.stream().filter( name -> !("snow".equals(name) ) ).collect(Collectors.toList());System.out.println(list);

filter 过滤会保留满足条件的。

结果如下:

[nier, sar, juaya, rock, nier]



在这里插入图片描述




http://www.ppmy.cn/news/1374567.html

相关文章

04. Nginx入门-Nginx WEB模块

测试环境 此处使用的yum安装的Nginx路径。 此处域名均在本地配置hosts。 主配置文件 路径&#xff1a;/etc/nginx/nginx.conf user nginx; worker_processes auto;error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid;events {worker_connection…

【简说八股】Redisson的守护线程是怎么实现的

Redisson Redisson 是一个 Java 语言实现的 Redis SDK 客户端&#xff0c;在使用分布式锁时&#xff0c;它就采用了「自动续期」的方案来避免锁过期&#xff0c;这个守护线程我们一般也把它叫做「看门狗」线程。 Redission是一个在Java环境中使用的开源的分布式缓存和分布式锁实…

Python Tkinter GUI 基本概念

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd;如果停止&#xff0c;就是低谷&#xf…

excel中去除公式,仅保留值

1.单个单元格去除公式 双击单元格&#xff0c;按F9. 2.批量去除公式 选中列然后复制&#xff0c;选择性粘贴&#xff0c;选值粘贴

Vue3搭建后台管理系统模板

1、搭建后台管理系统模板 1.1项目初始化 从0开始搭建一个vue3版本的后台管理系统。一个项目要有统一的规范&#xff0c;需要使用eslintstylelintprettier来对我们的代码质量做检测和修复&#xff0c;需要使用husky来做commit拦截&#xff0c;需要使用commitlint来统一提交规范…

【深度学习】1. 深度学习概述

感知器模型 人脑中的神经元:一个神经元通常具有多个树突&#xff0c;主要用来接受传入信息;而轴突只有一条&#xff0c;轴突尾端有许多轴突末梢可以给其他多个神经元传递信息。轴突末梢跟其他神经元的树突产生连接&#xff0c;从而传递信号。 而在计算机的神经网络中&#xff…

浮点数和定点数

前言 大家好我是jiantaoyab&#xff0c;这是我所总结作为学习的笔记第七篇,在这里分享给大家,还有一些书籍《深入理解计算机系统》《计算机组成&#xff1a;结构化方法》《计算机体系结构&#xff1a;量化研究方法》&#xff0c;今天我们来了解定点数和浮点数 定点数 BCD编码 …

Flask入门四(信号、SQLAlchemy快速使用、SQLAlchemy原生sql操作、SQLAlchemy操作表)

文章目录 一、信号1.内置信号2.内置信号的使用步骤3.自定义信号4.Django中信号的使用 二、SQLAlchemy介绍和快速使用1.介绍2.sqlalchemy原生操作 三、sqlalchemy操作表1.创建、删除表2.简单操作&#xff08;orm&#xff09; 一、信号 信号量&#xff1a;Semaphore信号量可以理解…