Java基础系列(面试必备):Java之循环删除List元素的正确方法!

Java基础系列(面试必备):Java循环删除List元素正确方法!


前言

今天博主将为大家分享:Java基础系列(面试必备):Java循环删除List元素的正确方法!不喜勿喷,如有异议欢迎讨论!


上代码

贴出代码各位可以复制去试一下,有两个正确的删除方式和一些常见的错误方式注意看哦!

    package com.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 
 * @Description: List的正确删除remove的姿势
 * @ClassName: ListRemoveTest.java
 * @author ChenYongJia
 * @Date 2019年7月3日 12:25
 * @Email chen87647213@163.com
 */
public class ListRemoveTest {

	public static void main(String[] args) {

		/**
		 * 先声明一个集合
		 */
		List<String> list = new ArrayList<>(Arrays.asList("c1", "cy2", "c3", "cy4", "c5", "cy6", "c7", "cy8", "c9"));
		
		/**
		 * 正确的删除方式
		 * 通过 CopyOnWriteArrayList 解决了 List的并发问题。每次remove的都是复制出的list
		 */
		final CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<String>(list);
        for (String item : cowList) {
            if (item.equals("y")) {
                cowList.remove(item);
            }
        }
        
        System.out.print(cowList+"\n");

		/**
		 * 报错 java.util.ConcurrentModificationException
		 */
		for (String str : list) {
			if (str.contains("y")) {
				list.remove(str);
			}
		}
		
		System.out.print(list+"\n");

		/**
		 * 报错:下标越界 java.lang.IndexOutOfBoundsException
		 */
		int size = list.size();
		for (int i = 0; i < size; i++) {
			String str = list.get(i);
			if (str.contains("y")) {
				list.remove(i);
			}
		}

		System.out.print(list+"\n");
		
		/**
		 * 正常删除,每次调用size方法,损耗性能,不推荐
		 */
		for (int i = 0; i < list.size(); i++) {
			String str = list.get(i);
			if (str.contains("y")) {
				list.remove(i);
			}
		}

		System.out.print(list+"\n");
		
		/**
		 * 正常删除,推荐使用
		 */
		for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
			String str = ite.next();
			if (str.contains("y")) {
				ite.remove();
			}
		}

		System.out.print(list+"\n");
		
		/**
		 * 报错 java.util.ConcurrentModificationException
		 */
		for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
			String str = ite.next();
			if (str.contains("y")) {
				list.remove(str);
			}
		}

		System.out.print(list+"\n");
		
	}

}
一下解释会对应上述代码
  • for循环遍历list

    这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
  • 增强for循环

    这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。
  • iterator遍历

    这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误,所以使用iterator的remove方法才是正解。
  • 小结

    (1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。

    (2)循环删除list中多个元素的,应该使用迭代器iterator方式。

继续分析

报异常IndexOutOfBoundsException我们很理解,是动态删除了元素导致数组下标越界了。

  • 那ConcurrentModificationException呢?

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

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 通过代码我们发现 Itr 是 ArrayList 中定义的一个私有内部类,

  • 在 next、remove方法中都会调用 checkForComodification 方法,该方法的作用是判断 modCount != expectedModCount是否相等,如果不相等则抛出ConcurrentModificationException异常。

  • 每次正常执行 remove 方法后,都会对执行expectedModCount = modCount赋值,保证两个值相等,那么问题基本上已经清晰了,在 foreach 循环中执行 list.remove(item);,对 list 对象的 modCount 值进行了修改,而 list 对象的迭代器的expectedModCount 值未进行修改,因此抛出了ConcurrentModificationException异常。

取下个元素的时候都会去判断要修改的数量和期待修改的数量是否一致,不一致则会报错,而通过迭代器本身调用remove方法则不会有这个问题,因为它删除的时候会把这两个数量同步,而list则不然。搞清楚它是增加的for循环就不难理解其中的奥秘了。

List源码如下

在这里插入图片描述


到这里:Java基础系列(面试必备):Java循环删除List元素的正确方法!分享完毕了,快去试试吧!


最后

  • 更多参考精彩博文请看这里:《陈永佳的博客》

  • 喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈永佳

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值