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

news2024/9/21 12:23:01

文章目录

  • 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.coloradmin.cn/o/1480330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Executable and Linkable Format(ELF)

File layout ELF文件有两种视图。程序头表&#xff08;Program Header&#xff09;显示在运行时使用的段&#xff08;Segments&#xff09;&#xff0c;而节头表&#xff08;Section Header&#xff09;则列出了二进制文件的所有节&#xff08;Sections&#xff09;的集合。程…

SCT2633STER:4.5V-60V Vin,3A,高效降压DCDC转换器

•宽输入范围&#xff1a;4.5V-60V •高达3A的连续输出电流 •1.221V1%反馈参考电压 •集成220mΩ高压侧MOSFET •低静态电流为300uA •轻负载下的脉冲跳过模式&#xff08;PSM&#xff09; •最小接通时间100ns •内置12ms软启动时间 •简易内部补偿 •固定频率500KH…

使用Docker搭建一款实用的个人IT工具箱——It-Tools

作为程序员&#xff0c;在日常工作中&#xff0c;需要借助一些工具来提高我们工作效率&#xff0c;IT-Tools是为开发人员度身打造的一套便捷在线工具。它提供全面功能&#xff0c;使开发者能以更高效方式完成任务。经由IT-Tools&#xff0c;开发人员能轻松应对各类技术挑战&…

怎么抠图把把人物扣下来?简单快捷的抠图方法

相信很多新手小白在初入设计行业时&#xff0c;对于抠图怎么把人物扣下来都是一头雾水。抠图作为设计中常用的一种技术&#xff0c;能够帮助我们快速提取图片中的某个部分&#xff0c;进行合成或者修改。对于老手来说&#xff0c;抠图或许是再熟悉不过的操作&#xff0c;但对于…

WPF中如何设置自定义控件

1.圆角按钮的设置&#xff1a; 众所周知在WPF中自带有提示信息&#xff0c;当我问创建Button时&#xff0c;点击空格出现如下可选设置 带有小扳手&#x1f527;图标为相应的属性&#xff0c;如果Button有CornerRadius&#xff08;角半径&#xff09;属性就能够直接设置Button实…

Unity中URP实现水体(整理优化)

文章目录 前言一、优化水的深度1、我们把 水流动的方向 和 水深浅过渡值&#xff0c;整合到一个四维变量中2、修改 水体流动方向3、在片元着色器中&#xff0c;修改使用过渡变量 二、优化泡沫三、优化水下的扭曲1、修复原本扰动UV的计算 四、优化水面高光1、把高光强度、光滑度…

如何用ai智能写作?推荐5款优秀的AI写作神器

写作是件繁琐的事情&#xff0c;也是需要耗费精力和时间的&#xff0c;还好目前有了ai写作神器的出现&#xff0c;帮助我们解决了不少的写作难题。这些AI写作工具不仅可以帮助我们提高写作效率&#xff0c;还能够生成高质量的内容。在本文中&#xff0c;我将向你推荐5款优秀的A…

MATLAB中strel函数用法

目录 语法 说明 任意邻域形状 二维几何邻域形状 三维几何邻域形状 示例 创建正方形结构元素 创建线形结构元素 创建盘形结构元素 创建三维球形结构元素 参数说明 属性 strel函数的功能是形态学结构元素。 语法 SE strel(nhood) SE strel("diamond",r…

BUUCTF AWD-Test1

打开靶场是这个有些简陋的界面。 随便点点&#xff0c;找到这个东西。 看到ThinkPHP&#xff0c;思路瞬间清晰&#xff0c;老熟人了。这个就是ThinkPHP漏洞。根据版本我们去找一下poc。 /index.php/?sIndex/\think\View/display&content%22%3C?%3E%3C?php%20phpinfo();…

Git工具Clone项目报错:OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054

目录 问题现象&#xff1a; 解决方法&#xff1a; 问题现象&#xff1a; 今天在项目中使用git工具clone远程库的项目地址时&#xff0c;遇到了如下报错&#xff0c;导致无法克隆项目代码到本地&#xff1a; OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054 解决方法&#…

【从Python基础到深度学习】9.Python 语法基础

一、常量与变量 常量:程序中使用的具体的数、字符。在运行过程中&#xff0c;值无法更改 变量:表示一一个存储单元&#xff0c;其中存储的值可以修改 如&#xff1a;a5,b6 变量命名: 1、只能包含字母、数字、下划线 2、只能以字母、下划线开头 3、不要使用关键字作为变量名称 …

浅谈mysql mvcc

目录 前言 mvcc 是如何工作的&#xff1f; 数据的更新 前言 mvcc 与一个事物的隔离级别有关&#xff0c;未提交读永远读的是当前值&#xff0c;串行化是通过加锁实现&#xff0c;这两种隔离级别都与mvcc 没有任何关系。只要一提到mvcc应该想到的是读提交以及可重复读&#…

【YOLO系列】YOLOv9论文超详细解读(翻译 +学习笔记)

前言 时隔一年&#xff0c;YOLOv8还没捂热&#xff0c;YOLO系列最新版本——YOLOv9 终于闪亮登场&#xff01; YOLOv9的一作和v7一样。v4也有他。 他于2017年获得台湾省National Central University计算机科学与信息工程博士学位&#xff0c;现在就职于该省Academia Sinica的…

vue3+ts+vite使用mock数据

安装以下命令 npm i vite-plugin-mock --save-dev npm i mockjs --save-dev 在根路径下创建mock文件夹 mock\user.ts const menuList [{path: /system,component: Layout,name: system,meta: {title: 系统管理,icon: Setting,roles: [sys:manage]},children: [{path: /depar…

曾桂华:车载座舱音频体验探究与思考| 演讲嘉宾公布

智能车载音频 I 分论坛将于3月27日同期举办&#xff01; 我们正站在一个前所未有的科技革新的交汇点上&#xff0c;重塑我们出行体验的变革正在悄然发生。当人工智能的磅礴力量与车载音频相交融&#xff0c;智慧、便捷与未来的探索之旅正式扬帆起航。 在驾驶的旅途中&#xff0…

卷积神经网络基本概念补充

卷积&#xff08;convolution&#xff09;、通道&#xff08;channel&#xff09; 卷积核大小一般为奇数&#xff0c;有中心像素点&#xff0c;便于定位卷积核。 步长&#xff08;stride&#xff09;、填充&#xff08;padding&#xff09; 卷积核移动的步长&#xff08;stride…

【Qt学习】QLCDNumber的介绍与实例使用(倒计时功能)

文章目录 1. 介绍2. 实例 - QLCDNumber倒计时3. 资源文件 1. 介绍 QLCDNumber是Qt框架中用于显示数字的控件&#xff0c;它模拟了一个液晶数字显示屏。 在Designer界面中显示如下&#xff1a; 有以下 常用属性&#xff1a; 属性描述intValue获取或设置QLCDNumber显示的整数…

前端架构: 脚手架包管理工具之lerna的全流程开发教程

Lerna 1 &#xff09;文档 Lerna 文档 https://www.npmjs.com/package/lernahttps://lerna.js.org [请直达这个链接] 使用 Lerna 帮助我们做包管理&#xff0c;并不复杂&#xff0c;中间常用的命令并不是很多这里是命令直达&#xff1a;https://lerna.js.org/docs/api-referen…

nodejs配置环境变量后不生效(‘node‘ 不是内部或外部命令,也不是可运行的程序或批处理文件)

一、在我们安装Node.js后&#xff0c;有时候会遇到node命令不管用的情况&#xff0c;关键是在安装时候已经添加配置了环境变量&#xff0c;向下面这样 但是还是不管用&#xff0c;这是因为环境变量配置不正确&#xff0c;权重不够&#xff0c;或者是命令冲突导致&#xff0c;解…

SpringCloud搭建微服务之Consul服务配置

1. 概述 前面有介绍过Consul既可以用于服务注册和发现&#xff0c;也可以用于服务配置&#xff0c;本文主要介绍如何使用Consul实现微服务的配置中心&#xff0c;有需要了解如何安装Consul的小伙伴&#xff0c;请查阅SpringCloud搭建微服务之Consul服务注册与发现 &#xff0c…