谨记,不要在for循环中删除List集合中的元素!!!

谨记,不要在for循环中删除List集合中的元素!!!

前言

  大家来思考这样一个问题,我们应该如何删除List集合中的元素呢?
  如果我们想要删除集合中的元素,就必须要获取到集合中的每一个元素,那我们首先想到的就是使用for循环。如果你能和我一样能想到这里,那我觉得,泰裤辣。
  但是!!!无论在什么情况下,我们都不要在使用for循环遍历List集合的时候对集合中的元素进行增删改操作,这样是会出现bug的!
  而且这种情况在阿里开发手册中也是明令禁止的,如图所示。
在这里插入图片描述

  那么我为什么会这样说呢,让我们看这样一个实例,看看使用for循环来删除集合中的元素会出现什么乌龙。

实例

需求

  在一个元素类型为String的List集合中,有N个元素,删除这些元素中包含字符’‘a’'的元素。
错误答案1: 使用普通for循环(for-i)

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        //调用集合的add方法向集合中添加元素
//		list.add("a");
//		list.add("ab");
//		list.add("abc");
//		list.add("acd");
//		list.add("cba");

        //也可以调用Collection类中的addAll静态方法向list集合中一次性添加元素
        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

//		 方式一:for循环进行数据删除
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).contains("a")) {
                list.remove(i);
            }
        }

        System.out.println("删除后:" + list);
    }
}

运行后,我们可以看到以下结果:

删除前:[a, ab, bac, acd, abc]
删除后:[ab, acd]

可以发现,其中包含’a’字符的两个元素并没有被删除干净。

分析

  使用普通for循环遍历List集合的同时删除元素在运行过程中是不会报错的。但大多数场景下,我们是不能使用这种方式的,上边的结果也印证了这一点。虽然你的代码不会报错,运行也正常,但在本实例中不能实现我们的要求,那这么写就是bug。
  当删除索引为i的元素后,后边的元素会自动向前补位,即原来索引为i+1的元素现在变为了索引为i的元素,但是下一次循环取的索引是i+1,此时你以为取到的是原来索引为i+1的元素,其实取到是原来索引为i+2的元素。以此类推,我们会漏掉很多目标元素。
流程分析如下图所示:
在这里插入图片描述
  那么有人可能会说,既然不可以使用普通for循环,那比普通for循环高级的增强for循环肯定可以了吧。
  这里先给大家分享一道面试题:增强for循环与普通for循环的区别?
  增强for循环是for循环的简化版本,一般用于遍历数组和集合,内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作,foreach适用于循环次数未知,或者循环次数比较麻烦情况下使用效率更高,但是更为复杂的一些操作还是需要用到for循环。
  那么大家先看着,下面我来编写运行看一下结果。
错误答案2: 使用增强for循环(foreach)

package javabean;

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

        // 方式二:增强for循环删除元素
		for (String string : list) {
			if (string.contains("a")) {
				list.remove(string);
			}
		}
        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.yunhe.day0324.Test4.main(Test4.java:29)

  这里爆出了一个异常:ConcurrentModificationException(并发修改异常)。正如面试题所说,增强for循环只适用于集合的遍历输出,也是不能对元素进行增删改查操作的。
  那么会有小伙伴问了,既然两种常用的for循环都不行,那就没有其他方法删除list集合中的数据了吗?答案是肯定的了,肯定有啊。下面我来给大家介绍两种用来删除List集合中的元素的方法。

  正确方式1:迭代器删除LIst集合中的元素

package javabean;

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

        // 通过list对象获取其迭代器对象
		Iterator<String> iterator = list.iterator();

		//while循环 判断是否还有下一个元素
		while (iterator.hasNext()) {
		//while循环内代码可用下面一行lambda写法代替:
		//list.removeIf(s -> s.contains("a"));
		
		//判断元素中是否包含'a'字符
			if (iterator.next().contains("a")) {
			//包含则删除元素
				iterator.remove();
			}
		}

        System.out.println("删除后:" + list);
    }
}

  但是我们这么常用的for循环真的无法完成List集合元素的删除吗?
  当然答案是肯定的,万事没有绝对。首先我们来总结一下,上面我们为什么说for循环不能删除List集合中的元素。因为在遍历的过程中,我们删除了当前元素,后面的元素会自动向前补位,那这个时候索引会向后移动,那么刚才补位的那个元素就不会再进行判断,而会直接跳过,所以就会造成元素的遗漏。究其根本,导致bug的终极原因就是后面的元素自动向前补位。那我们有没有什么方法避免这种现象呢?
  大家跟我一起摇摇脑袋,把芝士摇出来,大家好,我叫没文化!
  做完运动大家有没有什么思路?从前往后遍历元素会向前补位?那我们从后面往前面遍历删除元素总不会再向前补位了吧。不会吧!不会吧!肯定不会有人想到这里吧,如果有那我只能说你可能就是下一个詹姆斯·高斯林,trust yourself!
  正确方式2:for循环倒叙遍历删除元素

package javabean;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

        // 方式四:for循环倒叙遍历删除元素
		for (int i = list.size() - 1; i >= 0; i--) {
			if (list.get(i).contains("a")) {
				list.remove(i);
			}
		}
        System.out.println("删除后:" + list);
    }
}

  感谢大家的阅读与支持,喜欢的点赞、关注加收藏哟!
  关注我后续更新,学习Java不迷路!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值