怎么在遍历的时候删除List元素呢?

转载自:【https://blog.csdn.net/hustwht/article/details/52181810】

【https://blog.csdn.net/superxlcr/article/details/51534428】




常见的遍历List的三种方法

  • 使用普通for循环遍历
  • 使用增强型for循环遍历
  • 使用iterator遍历

对于线程不安全的ArrayList类,怎么实现呢?

方法1:【成功,但是删除的时候会改变list的index索引和size大小,可能会在遍历时导致一些访问越界的问题,因此不是特别推荐】

public class Main {  
    public static void main(String[] args) throws Exception {  
        List<Integer> list = new ArrayList<>();  
        for (int i = 0; i < 5; i++)  
            list.add(i);  
        // list {0, 1, 2, 3, 4}  
        for (int i = 0; i < list.size(); i++) {  
            // index and number  
            System.out.print(i + " " + list.get(i));  
            if (list.get(i) % 2 == 0) {  
                list.remove(list.get(i));  
                System.out.print(" delete");  
                i--; // 索引改变!  不然输出结果与预期不符 
            }  
            System.out.println();  
        }  
    }  
}  

方法2:【报异常,第一个会被正常删除,之后调用remove() 时,会报java.util.C_M_E异常】

public class Main {  
    public static void main(String[] args) throws Exception {  
        List<Integer> list = new ArrayList<>();  
        for (int i = 0; i < 5; i++)  
            list.add(i);  
        // list {0, 1, 2, 3, 4}  
        for (Integer num : list) {  
            // index and number  
            System.out.print(num);  
            if (num % 2 == 0) {  
                list.remove(num);  
                System.out.print(" delete");  
            }  
            System.out.println();  
        }  
    }  
}  

方法3:【成功,调用Iterator的remove()方法,而不是内部类的集合的remove()】

public class Main {  
    public static void main(String[] args) throws Exception {  
        List<Integer> list = new ArrayList<>();  
        for (int i = 0; i < 5; i++)  
            list.add(i);  
        // list {0, 1, 2, 3, 4}  
        Iterator<Integer> it = list.iterator();  
        while (it.hasNext()) {  
            // index and number  
            int num = it.next();  
            System.out.print(num);  
            if (num % 2 == 0) {  
                it.remove();  
                System.out.print(" delete");  
            }  
            System.out.println();  
        }  
    }  
}  
那么对于线程安全的 CopyOnWriteArrayList类呢?


方法1:【与ArrayList一样】

public class Main {  
    public static void main(String[] args) throws Exception {  
        List<Integer> list = new CopyOnWriteArrayList<>();  
        for (int i = 0; i < 5; i++)  
            list.add(i);  
        // list {0, 1, 2, 3, 4}  
        for (int i = 0; i < list.size(); i++) {  
            // index and number  
            System.out.print(i + " " + list.get(i));  
            if (list.get(i) % 2 == 0) {  
                list.remove(list.get(i));  
                System.out.print(" delete");  
                i--; // 索引改变!  
            }  
            System.out.println();  
        }  
    }  
}  

方法2:【成功,与ArrayList不同,因为copy---List的设计保证他能避免 C_M_E异常的报出】

public class Main {  
    public static void main(String[] args) throws Exception {  
        List<Integer> list = new CopyOnWriteArrayList<>();  
        for (int i = 0; i < 5; i++)  
            list.add(i);  
        // list {0, 1, 2, 3, 4}  
        for (Integer num : list) {  
            // index and number  
            System.out.print(num);  
            if (num % 2 == 0) {  
                list.remove(num);  
                System.out.print(" delete");  
            }  
            System.out.println();  
        }  
    }  
}  

方法3:【报错,java.lang.UnsupportedOperationException 】

A:与ArrayList不同,由于CopyOnWriteArrayList的iterator是对其List的一个“快照”,因此是不可改变的,所以无法使用iterator遍历删除。

public class Main {  
    public static void main(String[] args) throws Exception {  
        List<Integer> list = new CopyOnWriteArrayList<>();  
        for (int i = 0; i < 5; i++)  
            list.add(i);  
        // list {0, 1, 2, 3, 4}  
        Iterator<Integer> it = list.iterator();  
        while (it.hasNext()) {  
            // index and number  
            int num = it.next();  
            System.out.print(num);  
            if (num % 2 == 0) {  
                it.remove();  
                System.out.print(" delete");  
            }  
            System.out.println();  
        }  
    }  
}  
综上, 当使用ArrayList时,我们可以使用iterator实现遍历删除;而当我们使用CopyOnWriteArrayList时,我们直接使用增强型for循环遍历删除即可,此时使用iterator遍历删除反而会出现问题。


补充 :

       java中,List在遍历的时候,如果被修改了会抛出java.util.ConcurrentModificationException错误。
eg:  主线程遍历list时,子线程向list添加元素。如果想保证遍历的同时向list添加元素呢?CopyOnWriteArrayList 。
Q:   CopyOnWriteArrayList  是可以保证线程安全的呢,为什么?
A:   CopyOnWriteArrayList里处理写操作(包括add、remove、set等)是先将原始的数据通过JDK1.6的Arrays.copyof()来生成一份新的数组,然后在新的数据对象上进行写,写完后再将原来的引用指向到当前这个数据对象(这里应用了常识1),这样保证了每次写都是在新的对象上(因为要保证写的一致性,这里要对各种写操作要加一把锁,JDK1.6在这里用了重入锁)。
      这样读操作就很快很安全,适合在多线程里使用,绝对不会发生ConcurrentModificationException 。
结论:  CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。
另一个避免添加同步代码但可以避免并发修改问题的方式,在调度任务中构建一个新的列表,然后将原来指向到列表上的引用赋值给新的列表。

为了更好理解ArrayList 的遍历同时删除元素,再加一个实例。

 public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));
    for(inti=0;i<list.size();i++){
           list.remove(i);
    }
    System.out.println(list);// [b,d]输出,与预期不一样
}
  public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","a", "b", "c", "d"));
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("a")) {
                list.remove(i);
            }
        }
        System.out.println(list);//[a,b,c,d]与预期不一致
    }

Q: 看上面的for( ; ; )循环,以为for循环使用迭代器实现的? NoNoNo!看下面例子:

//会抛出 C_M_E异常
 public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","a", "b", "c", "d"));
        for(String s:list){
             if(s.equals("a")){
                  list.remove(s);
             }
        }
}
//输出正确,但是.next()必须在.remove()之前调用。在一个foreach循环中,编译器会使.next()在删除元素之后被调用??判断hasNext()??
//因此就会抛出ConcurrentModificationException异常,
 public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","a", "b", "c", "d"));
        Iterator<String>
        iter = list.iterator();
        while(iter.hasNext()){
            String s = iter.next();//要先于remove()调用
                if(s.equals("a")){
                    iter.remove();
                }
            }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值