Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >带了一个 3 年的开发,不会循环删除 List 中的元素,心态崩了。。

带了一个 3 年的开发,不会循环删除 List 中的元素,心态崩了。。

原创
作者头像
Java技术栈
发布于 2023-02-06 09:11:40
发布于 2023-02-06 09:11:40
82500
代码可运行
举报
文章被收录于专栏:Java技术栈Java技术栈
运行总次数:0
代码可运行

大家好,我是栈长。

最近和某个朋友聊天,说他手下的一个开发,工作 3 年多了,一个需求的技术点,需要循环删除 List 中的元素,整了半天,说程序报错,不会弄。。

他挺无语的,和我倾诉,我说工作 3 年多也不至于吧,不会的话,在网上找找也能搞定啊,他说确实是的,这个开发挺难带的,简直崩溃!!

循环删除 List 中的元素,这个问题是有需要的注意点的,如果是个新手,确实会遇到一点麻烦,但工作 3 年多,我觉得应该不至于啊,好吧,这篇栈长就来梳理一下这其中的道道。


问题

比如有以下这个 List:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public List<String> initList = Arrays.asList("张三", "李四", "周一", "刘四", "李强", "李白");

怎么删除 List 中姓李的人?


方法

我来们分析下有可行的方法!

本文所有完整示例源代码已经上传:

https://github.com/javastacks/javastack

欢迎 Star 学习,后面 Java 示例都会在这上面提供!

1、普通 for 循环删除(不可靠)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
  * 普通 for 循环删除
  * @author: 栈长
  * @from: 公众号Java技术栈
  */
 @Test
 public void remove1() {
     List<String> list = new ArrayList(initList);
     for (int i = 0; i < list.size(); i++) {
         String str = list.get(i);
         if (str.startsWith("李")) {
             list.remove(i);
         }
     }
     System.out.println(list);
 }

输出结果:

[张三, 周一, 刘四, 李白]

WC,李白没删干净?

问题就出在 list.size(),因为 list.size() 和 i 都是动态变化的,i 的值一直在累加,list.size() 一直在减少,所以 list 就会早早结束了循环。

所以这种方式虽然不会报错,但存在隐患,并且不容易被察觉,不建议使用。

2、普通 for 循环提取变量删除(抛异常)

把上面的示例中的 size 提出变量:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
  * 普通 for 循环删除(size提出变量)
  * @author: 栈长
  * @from: 公众号Java技术栈
  */
 @Test
 public void remove2() {
     List<String> list = new ArrayList(initList);
     int size = list.size();
     for (int i = 0; i < size; i++) {
         String str = list.get(i);
         if (str.startsWith("李")) {
             list.remove(i);
         }
     }
     System.out.println(list);
 }

输出结果:

好家伙,都直接干下标溢出异常了。。

这里也很明显,因为 size 变量是固定的,但 list 的实际大小是不断减小的,而 i 的大小是不断累加的,一旦 i >= list 的实际大小肯定就异常了。

3、普通 for 循环倒序删除(可靠)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
  * 普通 for 循环倒序删除
  * @author: 栈长
  * @from: 公众号Java技术栈
  */
 @Test
 public void remove3() {
     List<String> list = new ArrayList(initList);
     for (int i = list.size() - 1; i > 0; i--) {
         String str = list.get(i);
         if (str.startsWith("李")) {
             list.remove(i);
         }
     }
     System.out.println(list);
 }

输出结果:

[张三, 周一, 刘四]

结果输出正常,这种删除方式就算把 list.size() 提出变量也是 OK 的,因为循环中只用到了一次。

4、增强 for 循环删除(抛异常)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
  * 增强 for 循环删除
  * @author: 栈长
  * @from: 公众号Java技术栈
  */
 @Test
 public void remove3() {
     List<String> list = new ArrayList(initList);
     for (String element : list) {
         if (element.startsWith("李")) {
             list.remove(element);
         }
     }
     System.out.println(list);
 }

输出结果:

好家伙,又抛异常了。不过这次的异常和上面的下标异常不太一样,这次是:

java.util.ConcurrentModificationException

这个是集合操作中很常见的异常之一,即并发修改异常!

其实,for(xx in xx) 就是增强的 for循环,即迭代器 Iterator 的加强实现,其内部是调用的 Iterator 的方法,为什么会报 ConcurrentModificationException 错误,我们来看下源码:

取下个元素的时候都会去判断要修改的数量(modCount)和期待修改的数量(expectedModCount)是否一致,不一致则会报错,而 ArrayList 中的 remove 方法并没有同步期待修改的数量(expectedModCount)值,所以会抛异常了。

另外,最新最全的 Java 面试题整理好了,共 1800+,微信搜索Java面试库小程序在线刷题。

5、迭代器循环迭代器删除(可靠)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
  * 迭代器循环删除(iterator.remove)
  * @author: 栈长
  * @from: 公众号Java技术栈
  */
 @Test
 public void remove4() {
     List<String> list = new ArrayList(initList);
     for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
         String str = iterator.next();
         if (str.contains("李")) {
             iterator.remove();
         }
     }
     System.out.println(list);
 }

输出结果:

[张三, 周一, 刘四]

结果输出正常,这是因为迭代器中的 remove 方法将期待修改的数量(expectedModCount)值进行了同步:

所以,这种删除方法是安全的,推荐使用。

6、迭代器循环集合删除(抛异常)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
  * 迭代器循环删除(list.remove)
  * @author: 栈长
  * @from: 公众号Java技术栈
  */
 @Test
 public void remove5() {
     List<String> list = new ArrayList(initList);
     for (Iterator<String> ite = list.iterator(); ite.hasNext(); ) {
         String str = ite.next();
         if (str.contains("李")) {
             list.remove(str);
         }
     }
     System.out.println(list);
 }

输出结果:

又是那个并发修改异常,这个示例虽然使用了 Iterator 循环,但删除的时候却使用了 list.remove 方法,同样是有问题的,注意了,千万别用错了。

7、集合 forEach 方法循环删除(抛异常)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**
  * list.forEach 删除
  * @author: 栈长
  * @from: 公众号Java技术栈
  */
 @Test
 public void remove6() {
     List<String> list = new ArrayList(initList);
     list.forEach((e) -> {
         if (e.contains("李")) {
             list.remove(e);
         }
     });
     System.out.println(list);
 }

输出结果:

forEach 源码:

forEach 方法的背后其实就是增强的 for 循环,底层即迭代器,所以使用 list.remove 同样抛出 ConcurrentModificationException 异常。

8、stream filter 过滤(可靠)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * stream filter 过滤
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Test
public void remove7() {
    List<String> list = new ArrayList(initList);
    list = list.stream().filter(e -> !e.startsWith("李")).collect(Collectors.toList());
    System.out.println(list);
}

输出结果:

[张三, 周一, 刘四]

Stream filter 的详细用法,栈长之前已经分享过:你还在遍历搜索集合?别逗了!Java 8 一行代码搞定,是真的优雅!,没看过的点回去看看吧。

这个方法即是利用了 Stream 的筛选功能,快速过滤所需要的元素,虽然不是进行集合删除,但达到了同样的目的,这种方法要更简洁吧。

本文所有完整示例源代码已经上传:

https://github.com/javastacks/javastack

欢迎 Star 学习,后面 Java 示例都会在这上面提供!

总结

本文总结了 8 种循环删除 List 元素的方法:

  • 普通 for 循环删除(不可靠)
  • 普通 for 循环提取变量删除(抛异常)
  • 普通 for 循环倒序删除(可靠)
  • 增强 for 循环删除(抛异常)
  • 迭代器循环迭代器删除(可靠)
  • 迭代器循环集合删除(抛异常)
  • 集合 forEach 方法循环删除(抛异常)
  • stream filter 过滤(可靠)

可靠、可行的方案也只有 3 种,正统的删除法也就 2 种,但也要注意别用错了方法,所以也理解那个小伙子说怎么搞都搞不定,估计也是走了不少弯路,或者没有找对方法吧。

你还知道哪些删除技巧?欢迎留言分享~

好了,今天的分享就到这里了,后面栈长会分享更多好玩的 Java 技术和最新的技术资讯

最后,留个话题:

怎么去除 List<String> 中的重复元素呢?

下期分享,等栈长写完,公众号Java技术栈第一时间推送,不要走开~

版权声明: 本文系公众号 "Java技术栈" 原创,转载、引用本文内容请注明出处,抄袭、洗稿一律投诉侵权,后果自负,并保留追究其法律责任的权利。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
暂无评论
推荐阅读
编辑精选文章
换一批
ArrayList的删除姿势你都知道了吗
前几天有个读者由于看了《ArrayList哪种遍历效率最好,你真的弄明白了吗?》问了个问题普通for循环ArrayList为什么不能删除连续重复的两个元素?其实这个描述是不正确的。正确的应该是普通for循环正序删除,不能删除连续的元素所以就产生了这个文章。
java金融
2020/06/23
8460
ArrayList的删除姿势你都知道了吗
面试官:怎么去除 List 中的重复元素?我一行代码搞定,赶紧拿去用!
上次栈长给大家分享了《带了一个 3 年的开发,不会循环删除 List 中的元素,我简直崩溃!!》,上次也给大家留了个小话题:
Java技术栈
2023/02/27
1.2K0
面试官:怎么去除 List 中的重复元素?我一行代码搞定,赶紧拿去用!
jdk源码分析之List--使用中的坑
之前讲解了一篇 jdk源码分析之List--常用实现类分析与对比,讲述了常用List实现类以及使用方式和性能对比,那么此篇文章针对List使用过程中遇到的一些坑做一些总结和分析。
叔牙
2020/11/19
4490
jdk源码分析之List--使用中的坑
竟然有一半的人不知道 for 与 foreach 的区别???
之前有一个同事突然我问了我一个问题,说在foreach当中能不能删除list里面的元素,我当时大概说了一下是否能删除,以及原因;接下来我们来探讨一下是否能够如此; 遍历元素 首先,我们一一段代码为例: String[] array = {"1", "2", "3"}; for (String i : array) {     System.out.println(i); } ArrayList<String> list = new ArrayList<>(); list.add("111"); list.
程序猿DD
2022/05/10
2870
ArrayList在foreach删除倒数第二个元素不抛并发修改异常的问题
平时我们使用ArrayList比较多,但是我们是否知道ArrayList在进行foreach的时候不能直接通过list的add或者move方法进行删除呢,
小勇DW3
2018/09/29
1.7K0
遍历数据时arraylist效率高于linkedlist_遍历问题种类
一个 java 程序猿比较广为人知的小知识 ,是 ArrayList 和 LinkedList 最好使用迭代器删除,而不是遍历删除。
全栈程序员站长
2022/09/23
7280
遍历数据时arraylist效率高于linkedlist_遍历问题种类
List中remove()方法的陷阱,被坑惨了!
Java的List在删除元素时,一般会用list.remove(o)/remove(i)方法。在使用时,容易触碰陷阱,得到意想不到的结果。总结以往经验,记录下来与大家分享。
好好学java
2021/10/09
6480
java中集合类中Collection接口中的List接口的常用方法熟悉
本文主要介绍了Java中的List接口以及其常用的实现类,包括ArrayList、LinkedList、Vector、Stack和HashSet。List接口是Java集合框架中的基础接口,它提供了对集合对象的添加、删除、查找、迭代等操作。ArrayList和LinkedList是List接口的两个常用实现类,其中ArrayList是线程不安全的,而LinkedList是线程安全的。另外,还介绍了Collection接口和Iterator接口,以及它们的常用实现类。通过本文的学习,可以掌握Java集合框架中的List接口和Collection接口的基本用法,以及它们的常见实现类。
别先生
2017/12/29
8070
java中集合类中Collection接口中的List接口的常用方法熟悉
ArrayList的删除姿势你都掌握了吗
前几天有个读者由于看了《ArrayList哪种遍历效率最好,你真的弄明白了吗?》问了个问题普通for循环ArrayList为什么不能删除连续重复的两个元素?其实这个描述是不正确的。正确的应该是普通for循环正序删除,不能删除连续的元素所以就产生了这个文章。
java金融
2020/08/05
5700
ArrayList的删除姿势你都掌握了吗
你真的会写for循环吗?这 11 个 for 循环优化你得会...
日常开发中,经常会遇到一些循环耗时计算的操作,一般也都会采用 for 循环来处理,for 作为编程入门基础,主要是处理重复的计算操作,虽然简单好用,但在写法上也有很多的考究,如果处理不好,可能成为拖垮程序的罪魁祸首,下面总结 10 多条 for 循环的常见优化方式,让代码优雅起来。
一行Java
2023/11/01
1.3K0
你真的会写for循环吗?这 11 个 for 循环优化你得会...
ArrayList哪种循环效率更好你真的清楚吗
既然有4种遍历,那我们看看哪种遍历效率下面我们通过一个实验来看下这四种循环的耗时吧: 测试代码
java金融
2020/06/03
7380
ArrayList哪种循环效率更好你真的清楚吗
for 、foreach 、iterator 三种遍历方式的比较
for、foreach循环、iterator迭代器都是我们常用的一种遍历方式,你可以用它来遍历任何东西:包括数组、集合等
cxuan
2019/06/03
2.1K0
JAVA 集合list,Map删除元素的方法总结
这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
traffic
2020/04/09
3K0
Java,你告诉我 fail-fast 是什么鬼?
说起来真特么惭愧:十年 IT 老兵,Java 菜鸟一枚。今天我才了解到 Java 还有 fail-fast 一说。不得不感慨啊,学习真的是没有止境。只要肯学,就会有巨多巨多别人眼中的“旧”知识涌现出来,并且在我这全是新的。
沉默王二
2019/10/31
5430
遍历ArrayList的过程中移除元素的方式
原因:在匹配到第一个要删除的元素并移除时,后面元素会往前移位,导致索引位置改变,从而漏掉后面一个元素。
全栈程序员站长
2022/09/15
6680
Java基础(02)-15总结对象数组,集合Collection,集合List
3:集合(List)(掌握) (1)List是Collection的子接口 特点:有序(存储顺序和取出顺序一致),可重复。 package cn.itcast_01; import java.util.Iterator; import java.util.List; import java.util.ArrayList; /* * 需求:List集合存储字符串并遍历。 */ public class ListDemo { public static void main(String[] args)
Java帮帮
2018/03/16
8760
Java基础(02)-15总结对象数组,集合Collection,集合List
List集合
  Collection接口没有提供直接的实现类,而是提供了更加具体的子接口的实现类,其中一个最常用的子接口就是List接口。List集合中的元素是有序、可重复的。
别团等shy哥发育
2023/02/25
6980
List集合
Java 循环删除list中指定元素
由结果分析可知,利用for遍历删除,删除某个元素之后,list的大小发生变化,索引也发生变化,当删除索引为1的b之后,继续根据索引访问索引为2的元素,由于删除的缘故后面元素自动往前移动一位,故此时索引2上的元素已经不再是c,而变成了f,也就是如图所打印出来的结果,故for循环适合删除特定一位元素,不适合循环删除特定元素;
六月的雨在Tencent
2024/03/28
3380
Java 循环删除list中指定元素
面试官:怎么删除 HashMap 中的元素?我一行代码搞定,赶紧拿去用!
这两篇文章确实能帮助一大部分人,其中分享的一些实现技巧,编程很多年的高手也不一定用过,不管自己水平多牛,还是多谦虚好学一些,掌握多一点总不是什么坏事。
Java技术栈
2023/03/08
1.5K0
面试官:怎么删除 HashMap 中的元素?我一行代码搞定,赶紧拿去用!
List集合的坑
经过几年的工作经验,我发现List有很多坑,之前公司有些实习生一不小心就踩到了,所以我打算写一篇文章总结一下,希望看到这篇文章的人能不再踩到坑,代码没bug。做个快乐的程序员。
java技术爱好者
2020/09/22
9970
推荐阅读
相关推荐
ArrayList的删除姿势你都知道了吗
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验