欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

遍历删除list

程序员文章站 2022-05-19 17:50:47
...

    在Java中有时候我们会需要对List里面的符合某种业务的数据进行删除,但是如果不了解里面的机制就容易掉入“陷阱”导致遗漏或者程序异常。本文以代码例子的方式进行说明该问题
    1.采用Foreach循环遍历的方式
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>(); 
        list.add("Test1"); 
        list.add("Test2"); 
        list.add("Test3"); 
        list.add("Test4"); 
        list.add("隐藏"); 
        for (String string : list) {
            if( StringUtils.isNotBlank(string)&& (string).contains("隐藏")){ 
                list.remove(string);
          } 
        }
        for(String s : list){ 
            System.out.println(s); 
        } 
    }
    输出结果:
    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 com.suning.test.TestMain.main(TestMain.java:23)
    从运行结果看到程序抛ConcurrentModificationException。
    JDK的API中对该异常描述为:
    public class ConcurrentModificationException extends RuntimeException当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
    例如,某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的结果是不确定的。
    如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败迭代器,
    因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。
    注意,此异常不会始终指出对象已经由不同 线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常。
    例如,如果线程使用快速失败迭代器在 collection 上迭代时直接修改该 collection,则迭代器将抛出此异常。
    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。
    快速失败操作会尽最大努力抛出 ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,
    正确做法 是:ConcurrentModificationException 应该仅用于检测 bug。
    Java中的For each实际上使用的是iterator进行处理的。而iterator是不允许集合在iterator使用期间删除的。所以导致了iterator抛出了ConcurrentModificationException 。
    2.在遍历List过程中Iterator删除元素的做法:
        public static void main(String[] args) {
        List<String> list = new ArrayList<String>(); 
        list.add("Test1"); 
        list.add("Test2"); 
        list.add("Test3"); 
        list.add("Test4"); 
        list.add("隐藏"); 
        for(Iterator<String> it = list.iterator();it.hasNext();){ 
            String s=it.next();
            if( StringUtils.isNotBlank(s)&& (s).contains("隐藏")){ 
                it.remove();
            } 
        }
        for(String s : list){ 
            System.out.println(s); 
        } 
    }
    输出结果:
    Test1
    Test2
    Test3
    Test4
    对于iterator的remove()方法,也有需要我们注意的地方:
    1、每调用一次iterator.next()方法,只能调用一次remove()方法。
    2、调用remove()方法前,必须调用过一次next()方法。
    以下是JDK-API中对于remove()方法的描述:
    void remove()
    从迭代器指向的集合中移除迭代器返回的最后一个元素(可选操作)。每次调用 next 只能调用一次此方法。
    如果进行迭代时用调用此方法之外的其他方式修改了该迭代器所指向的集合,则迭代器的行为是不明确的。
    抛出:UnsupportedOperationException - 如果迭代器不支持 remove 操作。
    IllegalStateException - 如果尚未调用 next 方法,或者在上一次调用 next 方法之后已经调用了remove 方法。
    3.用for循环遍历删除数组元素
        public static void main(String[] args) {
        List<String> list = new ArrayList<String>(); 
        list.add("Test1"); 
        list.add("Test2"); 
        list.add("Test3"); 
        list.add("Test4"); 
        list.add("隐藏"); 
        list.add("隐藏2"); 
        list.add("隐藏3");
        list.add("隐藏4");  
        for (int i = 0; i < list.size(); i++) {
            if (StringUtils.isNotBlank(list.get(i)) && list.get(i).contains("隐藏")) {
                list.remove(i);
            }
        }
        for(String s : list){ 
            System.out.println(s); 
        } 
    }
    输出结果:
    Test1
    Test2
    Test3
    Test4
    隐藏2
    隐藏4
    我们是想删除包含“隐藏”的元素,但结果显示只删除了一个,另一个“隐藏”被遗漏了,
    原因是:删除了第一个“隐藏”后,集合里的元素个数减1,后面的元素往前移了1位,导致了第二个2被遗漏了。