在 Java 中从 for 循环中移出元素的原理及解决方案

在 Java 中从 for 循环中移出元素的原理及解决方案

在 Java 编程中,从集合中移除元素是常见操作。然而,直接在 for 循环中删除或移出元素往往会导致 ConcurrentModificationException(数组下标越界)。本文将详细解释这种情况发生的原因以及如何正确、安全地移出元素。

原理解释

Java 的集合框架如 ArrayList、HashSet 等在进行遍历时,通过创建一个迭代器来跟踪当前遍历的位置。这个迭代器会维护一个称为“修改计数器”(modCount)的变量,用于记录集合的结构性修改(例如添加或删除元素)。

在遍历过程中,如果集合的 modCount 与迭代器的 modCount 不一致,迭代器会检测到这一情况,并抛出 ConcurrentModificationException。这是一种快速失败机制,旨在防止在遍历时修改集合,避免可能的数据不一致和其他潜在问题。

错误示例

直接在 for-each 循环中删除元素会导致 ConcurrentModificationException。以下示例展示了这种情况:

import java.util.ArrayList;
import java.util.List;

public class RemoveElement {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("one");
        list.add("two");
        list.add("three");

        // 直接在for循环中移出元素,会抛出ConcurrentModificationException
        for (String item : list) {
            if (item.equals("two")) {
                list.remove(item); // 抛出异常
            }
        }
    }
}

运行上述代码会抛出 ConcurrentModificationException,因为在迭代时修改了集合。

解决方案

为了解决这个问题,有几种常用的方法可以安全地在循环中移除元素:

1. 使用 Iterator 的 remove 方法

使用 Iterator 的 remove 方法是推荐的方式,它可以安全地在遍历时移出元素。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class RemoveElement {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("one");
        list.add("two");
        list.add("three");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            if (item.equals("two")) {
                iterator.remove(); // 安全移除元素
            }
        }

        System.out.println(list); // 输出 [one, three]
    }
}

在上述代码中,Iterator 的 remove 方法确保在迭代过程中安全地移除元素。

2. 使用 ListIterator

对于 List 接口的实现,可以使用 ListIterator,它提供了 remove 方法,并且可以安全地在遍历时移出元素。

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class RemoveElement {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("one");
        list.add("two");
        list.add("three");

        ListIterator<String> iterator = list.listIterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            if (item.equals("two")) {
                iterator.remove(); // 安全移除元素
            }
        }

        System.out.println(list); // 输出 [one, three]
    }
}

3. 使用普通 for 循环(倒序遍历)

对于 ArrayList 这种随机访问列表,可以使用倒序遍历的普通 for 循环来避免问题。这种方式不会触发 ConcurrentModificationException,因为在倒序遍历时删除元素不会影响未遍历的元素。

import java.util.ArrayList;
import java.util.List;

public class RemoveElement {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("one");
        list.add("two");
        list.add("three");

        for (int i = list.size() - 1; i >= 0; i--) {
            if (list.get(i).equals("two")) {
                list.remove(i); // 安全移除元素
            }
        }

        System.out.println(list); // 输出 [one, three]
    }
}

4. 使用 Stream 的过滤操作

在 Java 8 及以上版本中,可以使用 Stream 的过滤操作来创建一个新的集合,不包含要移除的元素。

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class RemoveElement {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("one");
        list.add("two");
        list.add("three");

        list = list.stream()
                   .filter(item -> !item.equals("two"))
                   .collect(Collectors.toList()); // 过滤元素并收集到新的列表

        System.out.println(list); // 输出 [one, three]
    }
}

总结

在 Java 中,直接在 for-each 循环中移除元素会导致 ConcurrentModificationException,因为集合的结构被修改而迭代器无法跟踪这些修改。解决方案包括:

  • 使用 Iterator 的 remove 方法。
  • 使用 ListIterator。
  • 使用倒序遍历的普通 for 循环。
  • 使用 Java 8 的 Stream 过滤操作。

这些方法确保在迭代过程中,集合的结构修改能被正确处理,避免异常的发生。通过理解这些原理和解决方案,可以编写更健壮和高效的代码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

02:vim的使用和权限管控

vim的使用 1、vim基础使用1.1、vim pathname 2、vim高级用法2.1、查找2.2、设置显示行号2.3、快速切换行2.4、 行删除2.5、行复制粘贴 3、权限管理3.1、普通用户和特权用户3.2、文件权限表示 vim是Linux中的一种编辑器&#xff0c;类似于window中的记事本&#xff0c;可以对创建…

TP8/6 更改后台入口地址admin改为myadmin 隐藏真实后台网址

原来www.xxx.com/admin 改后www.xxx.com/myadmin config/app.php // 应用映射&#xff08;自动多应用模式有效&#xff09;app_map > [admintest>admin],

【前端VUE】VUE3第一节—vite创建vue3工程

什么是VUE Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0…

卷积层里的填充和步幅

一、定义 1、对于卷积&#xff0c;我们另一个超参数是核的大小&#xff0c;通常使用的卷积核是33或者55&#xff0c;很少用偶数核 2、填充是为了让输出不变或者变大&#xff0c;是为了在输入不太大&#xff0c;又能使模型足够深的情况下使用 3、填充&#xff1a;在输入周围添…

开源软件开发平台哪家好?

进行数字化转型&#xff0c;离不开低代码技术平台等软件产品的加持与助力。因为它更好操作、更灵活、易维护等优势特点突出&#xff0c;在推动企业实现流程化办公的过程中助力明显&#xff0c;作用大&#xff0c;深得客户喜爱。那么&#xff0c;低代码技术平台、开源软件开发平…

居然这么简单就能实现扫雷游戏!

目录 一.思路 1.成果展示 2.思路 二.具体操作 1.创建"棋盘" 2.初始化雷 3.布置雷 4.打印 5.排除雷 三.代码实现 1.test.c文件 2.thunder.h文件 3.thunder.c文件 Hello&#xff0c;大家好&#xff0c;今天我们来实现扫雷游戏&#xff0c;希望这一篇博客能给带给大家一…

HCIA是什么等级的证书

HCIA是华为认证体系中的初级认证&#xff0c;主要用于表明持有者在某一技术领域达到了工程师级别的能力证明。这种认证主要涉及到ICT(信息技术基础设施与通信)设备的安装、配置、运行以及故障排除等方面。 HCIA证书的作用 HCIA证书是进入IT行业的一个初步资格证书&#xff0c;它…

昇思25天学习打卡营第6天|数据变换 Transforms

学习目标&#xff1a;熟练掌握数据变换操作 熟悉mindspore.dataset.transforms接口 实践掌握常用变换 昇思大模型平台学习心得记录&#xff1a; 一、关于mindspore.dataset.transforms 1.1 变换 mindspore.dataset.transforms.Compose将多个数据增强操作组合使用。 mindspo…

【Java】如果让你设计一个分布式链路跟踪系统?你怎么做?

一、问题解析 分布式链路跟踪服务属于写多读少的服务&#xff0c;是我们线上排查问题的重要支撑。我经历过的一个系统&#xff0c;同时支持着多条业务线&#xff0c;实际用上的服务器有两百台左右&#xff0c;这种量级的系统想排查故障&#xff0c;难度可想而知。 因此&#…

Linux文件传输

目录 wget传输 先用我的云服务器vps开放一个端口 目标机wget进行请求下载 指定保存路径 curl传输&#xff08;win11也有&#xff09; ​Netcat传输 文件传输 SFTP传输 连接远程主机 下载远程主机文件 DNS传输 dns带出字符数据命令 拼接hex码进行转字符串 成功转出​…

WIN32核心编程 - 数据类型 错误处理 字符处理

公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 数据类型 基本数据类型 Win32基本数据类型 错误处理 C语言中的错误处理 C中的错误处理 Win32中的错误处理 字符处理 C/C WIN32 字符处理 数据类型 基本数据类型 C/C语言定义了一系列…

umi项目中的一些趣事

前言 出于某些安全问题&#xff0c;需要把HTML中框架注入的umi版本信息去掉&#xff0c;那要怎么搞呢~ 方案 查找官方文档&#xff0c;没发现可以去掉注入信息的方法&#xff0c;但在一番折腾后&#x1f609;终究还是解决了~ 发现 版本信息是从这里注入的~ Object.define…

uboo对内存操作读写命令的基本使用

内存操作命令 直接对DRAM进行读写的操作,uboot常用的内存操作命令有md,nm,mm,mw,cp和cmp. md命令: 显示内存值 # md md - memory display Usage: md [.b, .w, .l, .q] address [# of objects] b:1个字节 byte w:2个字节 world l:4个字节 long of objects 以word 为单位的1…

人工智能--图像语义分割

个人主页&#xff1a;欢迎来到 Papicatch的博客 课设专栏 &#xff1a;学生成绩管理系统 专业知识专栏&#xff1a;专业知识 ​ 文章目录 &#x1f349;引言 &#x1f349;介绍 &#x1f348;工作原理 &#x1f34d;数据准备 &#x1f34d;特征提取 &#x1f34d;像素分…

实习总结 --- 其他业务

一. 回归测试&#xff1a;回归测试与测新是对应的&#xff0c;当需求准入交付测试的时候首先要进行的就是测新&#xff0c;也就是对新功能对测试&#xff0c;一般是在sim环境下测试的&#xff1b;当测新通过后才会进行回归测试&#xff0c;回归测试的目的是为了保证老功能的正确…

人工智能 (AI) 基本概念 入门篇【C#】版

1. 什么是人工智能&#xff1f; 人工智能&#xff08;Artificial Intelligence, AI&#xff09;是指计算机系统能够执行通常需要人类智能的任务&#xff0c;如视觉识别、语音识别、决策和语言翻译等。AI的核心是通过算法和数据进行学习和推理&#xff0c;以实现智能行为。 2.…

2.3.2 主程序和外部IO交互 (文件映射方式)----IO Client实现

2.3.2 主程序和外部IO交互 &#xff08;文件映射方式&#xff09;----IO Client C实现 和IOServer主要差别&#xff1a; 1 使用Open_Client 连接 2 一定要先打开IOServer&#xff0c;再打开IO_Client 效果显示 1 C 代码实现 1.1 shareddataClient.h 头文件中引用 和sharedd…

PTA-线性表实验(JAVA)

题目1&#xff1a;Josephus环的问题及算法 【实验内容】 编程实现如下功能&#xff1a; 题意说明&#xff1a;古代某法官要判决n个犯人的死刑&#xff0c;他有一条荒唐的法律&#xff0c;将犯人站成一个圆圈&#xff0c;从第start个犯人开始数起&#xff0c;每数到第distance的…

Redis基础教程(一):redis配置

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

信息安全体系架构设计

对信息系统的安全需求是任何单一安全技术都无法解决的&#xff0c;要设计一个信息安全体系架构&#xff0c;应当选择合适的安全体系结构模型。信息系统安全设计重点考虑两个方面&#xff1b;其一是系统安全保障体系&#xff1b;其二是信息安全体系架构。 1.系统安全保障体系 安…
最新文章