• Java 集合(List、Set)遍历、判断、删除元素时的小陷阱


    开发中,常有场景:遍历集合,依次判断是否符合条件,如符合条件则删除当前元素。

    不知不觉中,有些陷阱,不知你有没有犯。

    1. 一、漏网之鱼-for循环递增下标方式遍历集合,并删除元素

    如果你用for循环递增下标方式遍历集合,在遍历过程中删除元素,你可能会遗漏了某些元素。说那么说可能也说不清楚,看以下示例:

    复制代码
    import java.util.ArrayList;
    import java.util.List;
    
    public class ListTest_Unwork {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            System.out.println("Original list : " + list);
    
            String temp = null;
            for (int i = 0; i < list.size(); i++) {
                temp = list.get(i);
                
                System.out.println("Check for " + temp);
                if ("3".equals(temp)) {
                    list.remove(temp);
                }
            }
            System.out.println("Removed  list : " + list);
        }
    
    }
    复制代码

    日志打印:

    复制代码
    Original list : [1, 2, 3, 4, 5]
    Check for 1
    Check for 2
    Check for 3
    Check for 5
    Removed  list : [1, 2, 4, 5]
    复制代码

    如日志所见,其中值为4的元素并未经过判断,漏网之鱼。

    解决方法为以下两个(但一般不建议我们在遍历中用不是遍历本身的函数删除元素,见下节关于“ConcurrentModificationException”的内容):

    1、对于此情况,我一般都从后面开始遍历,以避免问题:

    复制代码
    import java.util.ArrayList;
    import java.util.List;
    
    public class ListTest_Work {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            System.out.println("Original list : " + list);
            System.out.println();
    
            String temp = null;
            for (int i = list.size() - 1; i >= 0; i--) {
                temp = list.get(i);
                
                System.out.println("Check for " + temp);
                if ("3".equals(temp)) {
                    list.remove(temp);
                }
            }
            System.out.println("Removed  list : " + list);
        }
    
    }
    复制代码

    2、直接从新创建一个集合,重新摆放,但消耗内存,慎用:

    复制代码
    import java.util.ArrayList;
    import java.util.List;
    
    public class ListTest_Work2 {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            System.out.println("Original list : " + list);
            System.out.println();
    
            List<String> tempList = new ArrayList<String>();
            for (String temp : list) {
                System.out.println("Check for " + temp);
                if (!"3".equals(temp)) {
                    tempList.add(temp);
                }
            }
            System.out.println("Removed  list : " + tempList);
        }
    
    }
    复制代码

    2. 二、ConcurrentModificationException异常-Iterator遍历集合过程中用其他手段(或其他线程)操作元素

    ConcurrentModificationException是Java集合的一个快速报错(fail-fast)机制,防止多个线程同时修改同一个集合的元素。在用Iterator遍历集合时,如果你用其他手段(非Iterator自身手段)操作集合元素,就会报ConcurrentModificationException。

    不信?用Iterator方式或简写的for(Object o : list) {}方式,遍历集合,修改元素时会报异常:

    复制代码
    import java.util.ArrayList;
    import java.util.List;
    
    public class ListTest2_Unwork {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            System.out.println("Original list : " + list);
            System.out.println();
    
            for (String temp : list) {
                System.out.println("Check for " + temp);
                if ("3".equals(temp)) {
                    list.remove(temp);
                }
            }
            System.out.println("Removed  list : " + list);
        }
    
    }
    复制代码

    复制代码
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class ListTest3_Unwork {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            System.out.println("Original list : " + list);
            System.out.println();
    
            Iterator<String> i = list.iterator();
            String temp = null;
            while (i.hasNext()) {
                temp = i.next();
                System.out.println("Check for " + temp);
                if ("3".equals(temp)) {
                    list.remove(temp);
                }
            }
            System.out.println("Removed  list : " + list);
        }
    
    }
    复制代码

     日志:

    复制代码
    Original list : [1, 2, 3, 4, 5]
    
    Check for 1
    Check for 2
    Check for 3
    Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
        at java.util.ArrayList$Itr.next(ArrayList.java:831)
        at ListTest3_Unwork.main(ListTest3_Unwork.java:20)
    复制代码

    在删除元素“3”时,会报异常。

    对于此情况,需要用iterator的remove方法替代,结果是妥妥的:

    复制代码
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class ListTest3_Work {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            System.out.println("Original list : " + list);
            System.out.println();
    
            Iterator<String> i = list.iterator();
            String temp = null;
            while (i.hasNext()) {
                temp = i.next();
                System.out.println("Check for " + temp);
                if ("3".equals(temp)) {
                    i.remove();
                }
            }
            System.out.println("Removed  list : " + list);
        }
    
    }
    复制代码

    延伸个小问题,为什么for(Object o : list) {}方式遍历集合,现象和Iterator方式一样,都会报错呢?

    答:这是因为Java的糖语法,“for(Object o : list) {}方式”只是Java语言用“易用性糖衣”吸引你的手段,本质上,它也是Iterator。不信,你写下下面这段程序,反编译看看就清楚了:

    package com.nichagil.test.forloop;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class ForTester {
        
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("a");
            
            for (String s : list) {
                list.remove(s);
                System.out.println(s);
            }
        }
    
    }
    复制代码
    package com.nichagil.test.forloop;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class ForTester {
        
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("a");
            
            for (String s : list) {
                list.remove(s);
                System.out.println(s);
            }
        }
    
    }
    复制代码

    反编译后是这样的:

    package com.nichagil.test.forloop;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class ForTester {
        public static void main(String[] args) {
            ArrayList list = new ArrayList();
            list.add("a");
            Iterator arg2 = list.iterator();
    
            while (arg2.hasNext()) {
                String s = (String) arg2.next();
                list.remove(s);
                System.out.println(s);
            }
    
        }
    }
    复制代码
    package com.nichagil.test.forloop;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class ForTester {
        public static void main(String[] args) {
            ArrayList list = new ArrayList();
            list.add("a");
            Iterator arg2 = list.iterator();
    
            while (arg2.hasNext()) {
                String s = (String) arg2.next();
                list.remove(s);
                System.out.println(s);
            }
    
        }
    }
  • 相关阅读:
    ABP实现文件下载
    sqlserver把查询结果转为json和xml
    使用docker安装nginx
    sqlserver 索引优化 sql语句执行分析
    centos7下安装docker教程
    mysql查看sql的执行计划(是否使用索引等)
    vue计算属性和watch的区别有哪些?
    多表连接的三种方式详解 hash join、merge join、 nested loop
    【Winform】所有的dll都打包到一个exe里
    【异常处理】System.Data.Entity.Core.EntityException: 'The underlying provider failed on Open.'
  • 原文地址:https://www.cnblogs.com/Bengi/p/7567724.html
Copyright © 2020-2023  润新知