java for 遍历(:方式)时操作的问题和其他遍历方式的思考

for循环下操作被循环列表是可能存在坑的,首先我们列出四种循环一个列表并删除一个元素的实现代码
我们执行的结果是,第一种直接报错,第二种没有报错但是存在隐藏坑,第三种和第四种都是可行的方案。
if (modCount != expectedModCount)
throw new ConcurrentModificationException();


import com.alibaba.fastjson.JSONObject;
import org.junit.Test;

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

/**
 * for循环测试
 * @author humorchen
 * @date 2023/1/10 18:41
 */
public class ForTest {
    @Test
    public void test1() {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        for (String s : list) {
            if ("b".equals(s)) {
                list.remove(s);
            }
        }
        System.out.println(JSONObject.toJSONString(list));
    }


    @Test
    public void test2() {
        ArrayList<String> list = new ArrayList<>();

        list.add("a");
        list.add("b");
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if ("b".equals(s)) {
                list.remove(i);
            }
        }
        System.out.println(JSONObject.toJSONString(list));
    }

    @Test
    public void test3() {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if ("b".equals(next)) {
                iterator.remove();
            }
        }
        System.out.println(JSONObject.toJSONString(list));
    }

    @Test
    public void test4() {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        String needRemove = null;
        for (String s : list) {
            if ("b".equals(s)) {
                needRemove = s;
                break;
            }
        }
        list.remove(needRemove);
        System.out.println(JSONObject.toJSONString(list));
    }
}

我们可以看到执行test1的结果是报错了

在这里插入图片描述

既然有报错那我们一定要搞清楚来龙去脉,我们可以看到报错时的运行栈指向了ArrayList中iterator的next方法,for循环在调用迭代器获取下一个元素时,next方法中第一行就是做了操作数检查checkForComodification();

在这里插入图片描述

那我们就来看看这个方法怎么搞的,我们可以看到代码如下

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

他检查了这个arraylist当前的modCount和expectedModCount,也就是当前的值和期望值是否相同,如果不同就抛出ConcurrentModificationException这个异常。
那我们继续追踪看这个期望值是什么时候设置上的,从我截图可以看到,在for循环:这种方式时,其实是去调用了iterator方法获取这个列表的迭代器来遍历,而这个迭代器又是ArrayList自己继承迭代器重写了一些方法,因此对ArrayList循环时要操作的话必须利用迭代器来操作删除,否则报错。
那有的同学就会说了,我可以用下标取删除不会报错啊,可是你要想到当你删除一个元素的时候,集合长度就变化了,那你后续下标再删除就对不上号了~而业务中删除是可能删除任何一个元素,删除任意多个的,即使没有我们也需要让我们写的代码鲁棒性强,避免后面修改的人不知道这些魔法操作而踩坑!

在这里插入图片描述

在这里插入图片描述

posted @ 2023-01-10 19:07  HumorChen99  阅读(4)  评论(0编辑  收藏  举报  来源