还在自己写迭代器进行remove?快来看看新方法

我们都知道 List 中是不允许在循环的过程中去进行移除元素的,为什么呢?一般的新人可能会遇到这个问题,比如说会从 List 的遍历的过程中去进行 remove 数据,但是干过几年的开发的有经验的工作人员,是肯定不会这么干的,很简单,会报错。

List 进行 remove

我们可以来看一段代码:

public static void main(String[] args){
List<String> list= new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");

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

System.out.println(list);
}

上面这段代码,一般都是初入开发行业的小伙伴可能会这么写,但是当你去运行的时候,就会发现会报一个错误。

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.example.fastdfs.Test.main(Test.java:22)

但是当我们把代码改成删除元素 2 的时候,发现又成功了!!!

是真的,成功了,我们看代码和运行结果:

public static void main(String[] args){
List<String> list= new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");

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

System.out.println(list);
}

运行结果如下:

为什么会出现这种情况,我删除第一个元素不行,删除第二个元素好用,删除第三个元素又不行了,到底是什么原因导致的呢?

这时候我们就得去看看他的源码编译出来是什么样子的。源码如下:

public static void main(String[] args){
List<String> list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
Iterator var2 = list.iterator();

while(var2.hasNext()){
String s =(String)var2.next();
if (s.equals("3")){
list.remove(s);
}
}

System.out.println(list);
}

也就是说,foreach 的循环内部,就是采用的iteratior形式,使用的核心方法是hasnext()和next()。

既然都使用迭代器了,为啥还是不行呢?我们来看看迭代器的源码,然后分析一下为啥不行.

其实我们可以从报错都能看出点端倪,报错信息是ArrayList.java:909 

checkForComodification(){
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

源代码在执行 remove 方法的时候后,也是调用的 list 当中的remove 方法,源代码中,就是这段:

public boolean remove(Object o){
if (o ==null){
for (int index =0; index < size; index++)
if (elementData[index]==null){
fastRemove(index);
return true;
}
} else {
for (int index =0; index < size; index++)
if (o.equals(elementData[index])){
fastRemove(index);
return true;
}
}
return false;
}

中间调用的 fastRemove 方法中,中间就看到了:

    private void fastRemove(int index){
modCount++;
int numMoved = size - index -1;
if (numMoved >0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}

这里我们的modCount++了

而当我们再一次循环的时候,调用的是list内部类itr的next方法,

在我们调用的list的remove的时候,modCount++了,而我们的expectedModCount是等于最开始modCount值.

这时候二者的值不相等的时候,就出现异常了。

归根结底,虽然这个地方使用的是迭代器的遍历,但是remove 的方法可不是迭代器的方法呀。

那么我们使用迭代器遍历然后移除是什么样子的呢?

    public static void main(String[] args){
List<String> list= new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");

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

System.out.println(list);
}

这么写是不是有点多,那么应该如何快速的写完这段代码呢?

其实一行代码就能很快解决这个事情,我们先来看代码怎么写的:

list.removeIf(vo->"3".equals(vo));

也不用管返回值了,只要是能满足这个条件的 ,那么就会从集合中给移除掉。

话不多说,看结果:

这么一看,是不是发现非常简单方便,而且还快捷,而且如果要是对于代码量来说的话,那肯定是非常的少的,但凡满足条件的,肯定可以。

removeIf 的进阶玩法

阿粉为什么称之为进阶玩法,实际上也并不是完整的进阶玩法,比如说如果我们有一个功能是这样的,要求做一个导入的功能,然后导入的数据只有一个车牌号是唯一值,之前导入的数据,不做处理,新增的文件中,可能会包含所有的数据,要求数据库中已经存在的数据,不处理,然后导入数据库中不存在的数据。

如果字段少的话,那么实现思路可能会有几种。

第一种:

mybatis 的 SelectKey 标签,判断是否存在,如果存在就不进行新增。

第二种:

导入之前,查询数据库数据,比对数据,然后直接进行remove,最后不存在的数据导入

这两种方法实际上都能实现,但是他们的适用情况就不太一样了,如果字段非常多呢?

自己写sql 的话,那么代价实在是有点大,如果你们使用的还是 Mybatis-plus 的话,那么肯定第一种方式好像就没办法使用了,只能使用第二种了。

那么我们的 removeIf 应该怎么来写呢?

//创建第一个UserList
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setName("张三");
user.setAge(20);
user.setDept("开发部");
userList.add(user);

User user1 = new User();
user1.setId(UUID.randomUUID().toString());
user1.setName("李四");
user1.setAge(22);
user1.setDept("测试部");
userList.add(user1);

User user2 = new User();
user2.setId(UUID.randomUUID().toString());
user2.setName("王五");
user2.setAge(27);
user2.setDept("财务部");
userList.add(user2);

//创建第二个UserList

List<User> userEnd= new ArrayList<>();
User user3= new User();
user3.setId(UUID.randomUUID().toString());
user3.setName("张三");
user3.setAge(20);
user3.setDept("开发部");
userEnd.add(user3);

User user4= new User();
user4.setId(UUID.randomUUID().toString());
user4.setName("李四");
user4.setAge(22);
user4.setDept("测试部");
userEnd.add(user4);

如果我们这时候要把第一个userList 中的数据导入数据库,而 userEnd 则是数据库中的数据,这时候,我们要根据姓名来区分的话,是不是应该之导入王五才对,这时候我们得筛选出王五的数据来,然后做导入,这个时候 removeIf 就派上用场了。

        userList.removeIf(us1-> userEnd.stream().anyMatch(u ->us1.getName().equals(u.getName())));

System.out.println(Arrays.toString(userList.toArray()));

我们最后来看看结果:

[User(id=029b0b0f-ad42-4c15-8341-a3bb401be6d6, name=王五, age=27, dept=财务部)]

是不是已经做到了呢?

你学会了么?

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/261439.html<

(0)
运维的头像运维
上一篇2025-05-03 15:35
下一篇 2025-05-03 15:36

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注