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

文章目录

  • 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.mzph.cn/news/712142.shtml

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

相关文章

python之计算CPI

CPI&#xff0c;即消费者物价指数&#xff08;Consumer Price Index&#xff09;&#xff0c;是一个反映居民家庭一般所购买的消费品和服务项目价格水平变动情况的宏观经济指标。它是在特定时段内度量一组代表性消费商品及服务项目的价格水平随时间而变动的相对数&#xff0c;通…

网络测试相关

前言 网络测试通常是指在网络环境比较复杂&#xff0c;而且有较多限制时&#xff0c;就需要清楚网络的走向和途径的节点&#xff0c;便于在出现问题时进行排查和优化网络性能&#xff0c;相关知识大多是计算机网络的 测试工具 抓包 wireshark 路由探测 traceroute/tracert 这…

云快充充电桩系统设计书

充电桩系统设计书 一、系统设计概述 随着新能源汽车市场的快速发展&#xff0c;充电桩作为电动汽车的重要配套设施&#xff0c;其市场需求日益增长。本系统旨在提供一套稳定、高效、易用的充电桩解决方案&#xff0c;以满足市场上新能源充电桩的主流需求。通过实现云快充V1.6协…

nginx使用详解--动静分离

什么是动静分离&#xff1f; 为了提高网站的响应速度&#xff0c;减轻程序服务器&#xff08;Tomcat&#xff0c;Jboss等&#xff09;的负载&#xff0c;对于静态资源&#xff0c;如图片、js、css等文件&#xff0c;可以在反向代理服务器中进行缓存&#xff0c;这样浏览器在请…

如何选择激活函数?

选择神经网络的激活函数是一个重要的决策&#xff0c;因为激活函数的选择会影响到网络的收敛速度以及能否有效地训练。不同的激活函数适用于不同的任务和网络架构。以下是选择激活函数时可以考虑的一些指导原则和常用的激活函数&#xff1a; 指导原则 问题类型&#xff1a; 二…

LLM小记

RAG 参考 检索增强生成(RAG)概念及优化 主流模型 llama系列 1. 背景&#xff1a;模型参数量级的积累&#xff0c;或者训练数据的增加&#xff0c;哪个对性能提升帮助更大&#xff1f; 最近的 “Training Compute-Optimal Large Language Models” 这篇论文提出一种缩放定…

Java:PDF图片抽取的两种方法

图片和PDF是我们日常生活和工作中经常接触到的文档格式。PDF是人们日常使用最多的跨平台文档&#xff0c;是一种用独立于应用程序、硬件、操作系统的方式呈现文档的文件格式。每个PDF文件包含固定布局的平面文档的完整描述&#xff0c;包括文本、字形、图形及其他需要显示的信息…

如何利用HubSpot海外获客系统实现海外市场扩张?

在当今全球化的时代&#xff0c;企业面临着越来越激烈的竞争&#xff0c;而海外市场则被视为获取更多增长机会的重要途径之一。针对这一挑战&#xff0c;企业需要建立一个完整的海外获客系统&#xff0c;而HubSpot软件的应用则成为了关键。作为HubSpot的合作伙伴&#xff0c;我…

librtmp源码分析

阅读了librtmp的源码&#xff0c;简单记录下。 首先补充下AMF格式基本知识 1 AMF格式 AMF是Action Message Format(动作消息格式)的简写&#xff0c;它是一种二进制的数据格式。它的设计是为了把actionscript里面的数据(包括Object, Array, Boolean, Number等)序列化成二进制…

jvm 基础知识和jvm 调优

类装载分为以下 5 个步骤&#xff1a; 加载&#xff1a;根据查找路径找到相应的 class 文件然后导入&#xff1b; 检查&#xff1a;检查加载的 class 文件的正确性&#xff1b; 准备&#xff1a;给类中的静态变量分配内存空间&#xff1b; 解析&#xff1a;虚拟机将常量池中的符…

C# 异步返回类型详解

在现代软件开发中&#xff0c;异步编程已经成为一种重要的编程范式&#xff0c;尤其是在需要与I/O密集型操作交互的上下文中&#xff0c;比如网络请求、数据库操作等。C# 语言提供了强大的异步支持&#xff0c;使得异步编程变得更加简单和直观。本文将详细介绍C#中异步返回类型…

vue3学习 【5】watch的使用

什么是watch 当我们需要根据一个数据的变化来进行一些操作的时候我们需要使用侦听器&#xff0c;它能够在响应式数据发生变化的时候触发提供的回调函数 基础侦听 watch 可以侦听不同的数据源。例如&#xff1a; ref计算属性响应式对象getter函数多个数据源组层的数据 cons…

20240229金融读报:央行阿拉善创新融资模式与碳排放权交易条例实施,新春政策聚焦新生产力及金融风险防范

1、人民银行阿拉善盟分行实例&#xff1a;再贷款产业链&#xff08;活体牲畜抵押贷款牲畜死亡率保险&#xff09;、“再贷款新型农牧业经营主体”&#xff08;基础设施薄弱、产业结构单一&#xff0c;筹集资金创办嘎查集体经济股份合作社贷款&#xff09; 2、建设银行实例&…

oracle11安装及使用

安装oracle11 官网下载地址 Oracle Database 11g Release 2 for Microsoft Windows (x64) 官网下载慢可访问我的资源 也可以网盘获取 链接&#xff1a;https://pan.baidu.com/s/1RDrGkqDA7tfKRnpJXUBMDw 提取码&#xff1a;z3na 上传安装包到服务器 在指定目录下创建文件…

adb命令

1. 常用命令&#xff1a; adb devices #查看连接设备adb -s cf27456f shell # 指定连接设备使用命令adb install test.apk # 安装应用adb install -r demo.apk #安装apk 到sd 卡&#xff1a;adb uninstall cn.com.test.mobile #卸载应用&#xff0c;需要指定包adb uninstall -…

Windows系统x86机器安装龙芯(loongarch64)3A5000虚拟机系统详细教程

本次介绍在window系统x86机器上安装loongarch64系统的详细教程。 1.安装环境准备。 首先&#xff0c;你得有台电脑。 配置别太差&#xff0c;至少4核8G内存&#xff0c;安装window10或者11都行&#xff08;为啥不能是Window7&#xff0c;你要用也不是不行&#xff0c;你先解决…

leetcode:860.柠檬水找零

题意&#xff1a;按照支付顺序&#xff0c;进行支付&#xff0c;能够正确找零。 解题思路&#xff1a;贪心策略&#xff1a;针对支付20的客人&#xff0c;优先选择消耗10而不是消耗5&#xff0c;因为5可以用来找零10或20. 代码实现&#xff1a;有三种情况&#xff08;代表三种…

vscode中使用python的cv2.imread()读取图片为None

这是之前的代码&#xff1a; import cv2img_path c:\\Users\\LENOVO\\Desktop\\QQ截图20240228234801.jpg print(img_path) print(cv2.__version__)img cv2.imread(img_path) print(img)运行结果&#xff1a; c:\Users\LENOVO\Desktop\QQ截图20240228234801.jpg 3.4.1 None…

Linux下主线程 return 0 和 pthread_exit(NULL) 的区别

1. 背景 在Linux环境下&#xff0c;主线程以return 0结束时&#xff0c;程序会在主线程运行完毕后结束。而当主线程以pthread_exit(NULL)作为返回值时&#xff0c;主线程会等待子线程结束后才会退出程序。本文将详细探讨这两种方式的区别&#xff0c;并提供相应的代码示例。 …

String类的使用

String常用的构造方法 String的源码 内部是一个数组和hash值&#xff0c;涉及到常量池后续补充&#xff08;常量池&#xff1a;存储相同的字符时只会存储一租&#xff09; String的比较 equals()与&#xff1a;String里面为我们提供了许多方法&#xff0c;可直接调用&#xf…