Java在for循环中修改集合

前天看到一篇文章什么?for循环也会出问题?,里面涉及到在for循环中修改集合,想起来自己刚入行的时候就碰到过类似的问题,于是复现了一下文章中的问题,并试验了其它在循环中修改集合的方法。

底层原理参考什么?for循环也会出问题?这篇文章的分析

1. 在fori中修改集合

  • 在fori中修改集合,不会抛出异常
  • 在fori中移除元素,部分元素可能会被跳过,无法被遍历到
  • 在fori中添加元素,遍历时元素不会被跳过,但如果添加的元素恰好满足添加元素的条件,可能导致无限循环

代码如下:

import java.util.ArrayList;
import java.util.List;

public class TestFor {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println("*****遍历时移除元素*****");
        for (int index = 0; index < list.size(); index++) {
            Integer num = list.get(index);
            System.out.println("当前遍历:" + num);
            if (num % 2 == 0) {
                list.remove(num);
                System.out.println("移除:" + num);
            }
        }
        list.forEach(o -> System.out.print(o + "\t"));

        System.out.println();
        list.clear();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println("*****遍历时添加元素*****");
        for (int index = 0; index < list.size(); index++) {
            Integer num = list.get(index);
            System.out.println("当前遍历:" + num);
            if (num % 2 == 0) {
                int addNum = 101 + num; // 让添加进去的addNum为奇数,防止后面都是偶数,导致无限循环
                list.add(addNum);
                System.out.println("添加:" + addNum);
            }
        }
        list.forEach(o -> System.out.print(o + "\t"));
    }
}

运行结果:

*****遍历时移除元素*****
当前遍历:1
当前遍历:2
移除:2
当前遍历:4
移除:4
1    3    5
*****遍历时添加元素*****
当前遍历:1
当前遍历:2
添加:103
当前遍历:3
当前遍历:4
添加:105
当前遍历:5
当前遍历:103
当前遍历:105
1    2    3    4    5    103    105

2. 在迭代器iterator中修改集合

  • 在iterator中通过iterator修改集合(remove),不会抛出异常
  • 在iterator中移除元素,元素不会被跳过,但iterator.remove()前,必须先执行iterator.next(),将next element的索引+1,否则会出现IllegalStateException
  • 使用iterator遍历元素时,跳过iterator直接去修改list,只要修改后再次执行iterator.next(),都会出现ConcurrentModificationException
  • 使用iterator遍历元素时,即便在最后一次遍历中(此时iterator.hasNext()为false)才执行list.add()或list.remove(),也会导致iterator.hasNext()从false变为true

代码如下:

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

public class TestFor {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println("*****遍历时移除元素*****");
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            // iterator.remove()前,必须先执行iterator.next(),将next element的索引+1,否则会出现IllegalStateException
            Integer num = iterator.next();
            System.out.println("当前遍历:" + num);
            if (num % 2 == 0) {
                iterator.remove();
                System.out.println("移除:" + num);
            }
        }
        list.forEach(o -> System.out.print(o + "\t"));

        System.out.println();
        list.clear();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println("*****遍历时修改list,修改后不执行iterator.next()*****");
        iterator = list.iterator();
        while (iterator.hasNext()) {
            Integer num = iterator.next();
            System.out.println("当前遍历:" + num);
            if (num == 5) {
                // list.add()、list.remove()都会导致iterator.hasNext()从false变为true
                Collections.sort(list);
                System.out.println("===>排序");
            }
        }
        list.forEach(o -> System.out.print(o + "\t"));
        System.out.println();
        list.clear();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println("*****遍历时修改list,修改后执行iterator.next()*****");
        iterator = list.iterator();
        while (iterator.hasNext()) {
            Integer num = iterator.next();
            System.out.println("当前遍历:" + num);
            if (num == 3) {
                Collections.sort(list);
                System.out.println("===>排序");
            }
        }
        list.forEach(o -> System.out.print(o + "\t"));
    }
}

运行结果:

*****遍历时移除元素*****
当前遍历:1
当前遍历:2
移除:2
当前遍历:3
当前遍历:4
移除:4
当前遍历:5
1    3    5
*****遍历时修改list,修改后不执行iterator.next()*****
当前遍历:1
当前遍历:2
当前遍历:3
当前遍历:4
当前遍历:5
===>排序
1    2    3    4    5
*****遍历时修改list,修改后执行iterator.next()*****
当前遍历:1
当前遍历:2
当前遍历:3
===>排序
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
    at http.TestFor.main(TestFor.java:56)

3. 在foreach中修改集合

查看编译后的.class后可知,foreach只是迭代器iterator的语法糖,编译后也是用iterator遍历,所以跟在迭代器iterator中修改集合一样

  • 在foreach中没有提供修改list的接口,在非最后一次遍历中修改list会出现ConcurrentModificationException
  • 使用foreach遍历元素时,只在最后一次遍历中修改list(不执行list.add()或list.remove(),而是其它操作比如排序),不会出现ConcurrentModificationException
  • 使用foreach遍历元素时,即便在最后一次遍历中才执行list.add()或list.remove(),也会出现ConcurrentModificationException

代码如下:

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

public class TestFor {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println("*****遍历时修改list,最后一次遍历时修改(不涉及list.add()、list.remove())*****");
        for (Integer num : list) {
            System.out.println("当前遍历:" + num);
            if (num == 5) {
                // 查看编译后的.class后可知,foreach只是iterator的语法糖,编译后也是用iterator遍历
                // 如果在最后一次遍历中执行list.add()或list.remove(),会导致ConcurrentModificationException
                Collections.sort(list);
                System.out.println("===>排序");
            }
        }
        list.forEach(o -> System.out.print(o + "\t"));

        System.out.println();
        System.out.println("*****遍历时修改list,在非最后一次遍历时修改*****");
        for (Integer num : list) {
            System.out.println("当前遍历:" + num);
            if (num == 3) {
                Collections.sort(list);
                System.out.println("===>排序");
            }
        }
        list.forEach(o -> System.out.print(o + "\t"));
    }
}

运行结果:

*****遍历时修改list,最后一次遍历时修改(不涉及list.add()、list.remove())*****
当前遍历:1
当前遍历:2
当前遍历:3
当前遍历:4
当前遍历:5
===>排序
1    2    3    4    5
*****遍历时修改list,在非最后一次遍历时修改*****
当前遍历:1
当前遍历:2
当前遍历:3
===>排序
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
    at http.TestFor.main(TestFor.java:27)

总结

  • 尽量不要在遍历中修改集合本身(修改集合中的元素的属性没问题),除非你能明确知道该操作导致的后果。
  • 如果需要在循环中移除元素,可以使用迭代器iterator。
Java中,使用for循环集合中添加数据的方式有多种。其中一种常见的错误方式是在循环内部创建对象,并将该对象添加到集合中。这样做会导致集合中的所有元素都是最后一个添加的对象。这是因为在Java中,对象是引用类型,当我们将一个对象添加到集合中时,实际上是将该对象的引用添加到集合中。而在循环中,每次迭代都会修改同一个对象的属性值,因此最终集合中的所有元素都指向同一个对象,且属性值都是最后一次迭代的结果。 为了解决这个问题,我们可以在每次迭代中创建一个新的对象,并将其添加到集合中。这样每个对象都是独立的,不会相互影响。以下是一种正确的方式: List<Person> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { Person ob = new Person(); ob.setAge(i); list.add(ob); } 在这个例子中,我们在每次迭代中都创建了一个新的Person对象,并将其添加到集合中。这样就确保了集合中的每个元素都是独立的,且具有不同的属性值。\[2\] #### 引用[.reference_title] - *1* *2* [javafor循环往List添加元素,导致所有元素全部是最后一个元素](https://blog.youkuaiyun.com/qq_30114277/article/details/118419178)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Javafor循環Map](https://blog.youkuaiyun.com/weixin_28949355/article/details/115989102)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值