集合操作进阶:关于移除列表元素的那点事

介绍

日常开发中,难免会对集合中的元素进行移除操作,如果对这方面不熟悉的话,就可能遇到 ConcurrentModificationException,那么,如何优雅地进行元素删除?以及其它方式为什么不行?

数据初始化
public static final List<String> list = new ArrayList<>();static {list.add("java");list.add("mysql");list.add("redis");list.add("spring");list.add("linux");list.add("git");
}
实现方式
方式一:普通 for
 @Testpublic void testRemoveFor() {list.add(2, "redis");System.out.println(list);for (int i = 0; i < list.size(); i++) {if ("redis".equals(list.get(i))) {list.remove(i);}}System.out.println(list);
}

这种方式虽然不会报错,但是会导致数据出现问题,比如 list: [java, mysql, redis, redis, spring, linux, git]

由于存在两个连续的 redis,当删除第一个时,后面的元素会向前填充,而迭代索引是一直增加的(即 i ),所以第二个 redis 没有经过检查就直接跳过了,导致最终数据为:[java, mysql, redis, spring, linux, git]

IDEA 其实也给出了我们提示(如下图):Suspicious ‘List.remove()’ in loop;想必就是因为这个原因。

在这里插入图片描述

方式二:增强 for
@Test
public void testRemoveForEach() {for (String str : list) {if ("redis".equals(str)) {list.remove(str);}}System.out.println(list);
}

这种方式就直接报错了:java.util.ConcurrentModificationException

将 .class 文件反编译后以上代码对应如下(将 .class 用 IDEA 打开即可):

Iterator var1 = list.iterator();
while(var1.hasNext()) {String str = (String)var1.next();if ("redis".equals(str)) {list.remove(str);}
}

从以上代码可以看出,for-each 实际上就是使用迭代器进行遍历元素,当在 for-each 中通过 java.util.ArrayList#remove 删除元素时,迭代器内部其实是不知道的,所以在执行 java.util.ArrayList.Itr#next() 操作时,会检查内部状态(modCount)是否一致,不一致则抛出。java.util.ConcurrentModificationException

方式三:迭代器
@Test
public void testRemoveIterator() {Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String str = iterator.next();if ("redis".equals(str)) {iterator.remove();}}System.out.println(list);
}

这种方式与上面 testRemoveForEach 相比,其实就只是将内部的 java.util.ArrayList#remove 换成了迭代器内部remove 方法,既然用的是迭代器自己的,那迭代器内部肯定就能够知道列表发生了变化,从而直接更新其内部状态,所以就不会报错了。

 /*** lastRet: 最后一次返回元素的索引* cursor:下一个返回元素的索引*/public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {// 调用 ArrayList 内部的 remove 进行删除元素,即 java.util.ArrayList#removeArrayList.this.remove(lastRet);// 将最后一次返回元素的索引赋值给下一个返回元素的索引(因为当前元素被删除后,后面的元素前移了)cursor = lastRet;lastRet = -1;// remove 之后,modCount 会加一,所以需要将 modCount 赋值给 expectedModCount,从而保持一致expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}
方式四:反向遍历
@Test
public void testRemoveForDesc() {for (int i = list.size() - 1; i >= 0; i--) {if ("redis".equals(list.get(i))) {list.remove(i);}}System.out.println(list);
}

通过倒序的方式遍历列表并删除元素,虽然这种方式也能够实现删除元素不报错,但从性能上考虑,当删除的元素比较多时,时间复杂度会变成 O(n^2),所以这种方式不建议使用。

方式五:removeIf
@Test
public void testRemoveIf() {list.removeIf("redis"::equals);System.out.println(list);
}

如果使用的是 JDK 1.8 及以上,建议使用这种方式删除元素,代码简洁优雅

查看源码,removeIf 底层其实就是使用迭代器的方式进行删除。

default boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter);boolean removed = false;final Iterator<E> each = iterator();while (each.hasNext()) {if (filter.test(each.next())) {each.remove();removed = true;}}return removed;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/20517.shtml

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

相关文章

国内类似ChatGPT的大模型应用有哪些?发展情况如何了

第一部分&#xff1a;几个容易混淆的概念 很多人&#xff0c;包括很多粉丝的科技博主&#xff0c;经常把ChatGPT和预训练大模型混为一谈&#xff0c;因此有必要先做一个澄清。预训练大语言模型属于预训练大模型的一类&#xff0c;而ChatGPT、文心一言又是预训练大语言模型的一个…

node基础-持续更新

node基础 1.node模块2.node环境搭建3.fs模块4.ES模块和CommonJS模块4.1 更改后缀名4.2 package.json配置支持es模块4.3 变量别名4.4 CommonJS模块 5.打造自己的脚手架工具5.1创建自定义全局指令5.2 使用commander处理--help参数5.3 处理自定义指令5.4 逻辑代码模块化拆分5.5 命…

iPad里的图片如何导出 iPad的照片如何管理

我们的设备中充满了各种重要的照片和视频&#xff0c;特别是iPad&#xff0c;作为苹果公司的一款强大的平板电脑&#xff0c;它不仅能够捕捉生活中的精彩瞬间&#xff0c;还可以存储和展示我们珍贵的回忆。然而&#xff0c;随着照片数量的不断增加&#xff0c;有效地管理和导出…

对boot项目拆分成cloud项目的笔记

引言&#xff1a;这里我用的是新版本的技术栈 spring-boot-starter-parent >3.2.5 mybatis-spring-boot-starter >3.0.3 mybatis-plus-boot-starter >3.5.5 spring-cloud-dependencies …

JVMの内存泄漏内存溢出案例分析

1、内存溢出 内存溢出指的是程序在申请内存时&#xff0c;没有足够的内存可供分配&#xff0c;导致无法满足程序的内存需求&#xff0c;常见的内存溢出情况包括堆内存溢出&#xff08;Heap Overflow&#xff09;和栈溢出&#xff08;Stack Overflow&#xff09;&#xff1a; …

AI去衣技术中的几何着色:揭秘数字时尚的魔法

在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度改变我们的生活&#xff0c;从智能家居到自动驾驶汽车&#xff0c;再到个性化医疗。然而&#xff0c;AI的影响远不止于此。它正在重塑我们对艺术、设计和时尚的理解。特别是在数字时尚领域&#…

Unity打包Webgl端进行 全屏幕自适应

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一&#xff1a;修改 index.html二&#xff1a;将非移动端设备&#xff0c;canvas元素的宽度和高度会设置为100%。三&#xff1a;修改style.css总结 下载地址&#x…

Solidity学习-投票合约示例

以下的合约有一些复杂&#xff0c;但展示了很多Solidity的语言特性。它实现了一个投票合约。 当然&#xff0c;电子投票的主要问题是如何将投票权分配给正确的人员以及如何防止被操纵。 我们不会在这里解决所有的问题&#xff0c;但至少我们会展示如何进行委托投票&#xff0c;…

《java数据结构》--详解Map和Set

概念 Map和set是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例化子类有关。在之前我们常见的搜索方式一般有两种&#xff1a; 一种是直接遍历&#xff0c;这种方法的时间复杂度为O(N)&#xff0c;如果元素比较多的话效率会十分低下另一种是…

Leecode---动态规划---打家劫舍 / 乘积最大子数组

动态规划法&#xff1a; 思路&#xff1a; &#xff08;1&#xff09;状态定义&#xff1a;dp[i]代表前i家能偷盗的最大金额 &#xff08;2&#xff09;状态初始化&#xff1a;如果只有一家&#xff0c;只能偷这家dp[0]nums[0]&#xff1b;如果有两家&#xff0c;因为是连通的&…

看图猜成语微信小程序源码

后台可以自行设置关卡、等级、也可以一键部署&#xff0c; 开通流量主之后实现躺赚&#xff0c;你懂得。 个人号也可以开通&#xff0c;审核一次性必过。 类目选择 教育&#xff0c;源码仅供您参考&#xff01; 源码下载 https://download.csdn.net/download/huayula/8938579…

【计算机毕业设计】基于SSM+Vue的网上花店系统【源码+lw+部署文档】

目录 目 录 1 绪论 1.1 选题背景 1.2 选题意义 1.3 研究内容 2 系统开发技术 2.1 Java语言 2.2 SSM框架 2.3 MYSQL数据库 2.4 Vue框架 3 系统分析 3.1可行性研究 3.1.1经济可行性 3.1.2时间可行性 3.1.3操作可行性 3.2系统性能分析 3.2.1系统易用性 3.2.2系统健壮性 3.2.3系统…

电脑没电关机,wsl和docker又挂了,附解决过程

如题&#xff0c;开了个会没带笔记本电源&#xff0c;点啊弄关机后docker打不开&#xff0c;我以为是docker坏了&#xff0c;结果docker报错&#xff1a; An unexpected error occurred while executing a WSL command. Either shut down WSL down with wsl --shutdown, and/or…

【蓝桥杯国赛】双指针

适用于以下的情境&#xff1a; ① 数组 / 字符串中&#xff0c;有多少个满足情况的连续区间。 ② 数组 / 字符串&#xff0c;合并。 【第十三届pythonB组试题&#xff1a;近似gcd】 1. 题目描述 2. 难度&#xff1a;⭐⭐⭐⭐ 3. 思考分析&#xff1a; 具体参考&#xff…

依据SAM大模型识别的英国农田(农业田野)边界(FIBOA)矢量数据集

简介 fiboa 是一项旨在提高农田边界数据互操作性和相关农业数据的合作计划。最近推出的 fiboa 不仅仅是一个规范&#xff0c;它还是一个全面的系统&#xff0c;包括符合规范的数据、不断完善规范的讨论以及积极促进其发展的活跃社区。本项目的重点是促进创建更多有关田地边界和…

黑马程序员——Spring框架——day03

目录&#xff1a; AOP AOP简介 问题导入AOP简介和作用【理解】AOP的应用场景为什么要学AOPAOP入门案例【重点】 问题导入AOP入门案例思路分析AOP入门案例实现AOP中的核心概念AOP工作流程【理解】 问题导入AOP工作流程AOP核心概念在测试类中验证代理对象AOP切入点表达式 问题导…

【python】爬虫记录每小时金价

数据来源&#xff1a; https://www.cngold.org/img_date/ 因为这个网站是数据随时变动的&#xff0c;用requests、BeautifulSoup的方式解析html的话&#xff0c;数据的位置显示的是“--”&#xff0c;并不能取到数据。 所以采用webdriver访问网站&#xff0c;然后从界面上获取…

C语言 指针——函数指针的典型应用:计算定积分

目录 梯形法计算函数的定积分 函数指针的典型应用 梯形法计算函数的定积分 函数指针的典型应用 用函数指针编写计算任意函数定积分的 通用 函数

Vivado 比特流编译时间获取以及FPGA电压温度获取(实用)

Vivado 比特流编译时间获取以及FPGA电压温度获取 语言 &#xff1a;Verilg HDL 、VHDL EDA工具&#xff1a;ISE、Vivado Vivado 比特流编译时间获取以及FPGA电压温度获取一、引言二、 获取FPGA 当前程序的编译时间verilog中直接调用下面源语2. FPGA电压温度获取&#xff08;1&a…