• 正确在遍历中删除List元素


    最近在写代码的时候遇到了遍历时删除List元素的问题,在此写一篇博客记录一下。

    一般而言,遍历List元素有以下三种方式:

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

    使用普通for循环遍历

    代码如下:
    [java] view plain copy
     
    1. public class Main {  
    2.     public static void main(String[] args) throws Exception {  
    3.         List<Integer> list = new ArrayList<>();  
    4.         for (int i = 0; i < 5; i++)  
    5.             list.add(i);  
    6.         // list {0, 1, 2, 3, 4}  
    7.         for (int i = 0; i < list.size(); i++) {  
    8.             // index and number  
    9.             System.out.print(i + " " + list.get(i));  
    10.             if (list.get(i) % 2 == 0) {  
    11.                 list.remove(list.get(i));  
    12.                 System.out.print(" delete");  
    13.                 i--; // 索引改变!  
    14.             }  
    15.             System.out.println();  
    16.         }  
    17.     }  
    18. }  

    结果如下:
    普通for循环遍历
     
    可以看到遍历删除偶数的结果是成功的,但是这种方法由于删除的时候会改变list的index索引和size大小,可能会在遍历时导致一些访问越界的问题,因此不是特别推荐。
     

    使用增强型for循环遍历

    [java] view plain copy
     
    1. public class Main {  
    2.     public static void main(String[] args) throws Exception {  
    3.         List<Integer> list = new ArrayList<>();  
    4.         for (int i = 0; i < 5; i++)  
    5.             list.add(i);  
    6.         // list {0, 1, 2, 3, 4}  
    7.         for (Integer num : list) {  
    8.             // index and number  
    9.             System.out.print(num);  
    10.             if (num % 2 == 0) {  
    11.                 list.remove(num);  
    12.                 System.out.print(" delete");  
    13.             }  
    14.             System.out.println();  
    15.         }  
    16.     }  
    17. }  

    结果如下:
    增强for循环遍历
     
    可以看到删除第一个元素时是没有问题的,但删除后继续执行遍历过程的话就会抛出ConcurrentModificationException的异常。
     

    使用iterator遍历

    [java] view plain copy
     
    1. public class Main {  
    2.     public static void main(String[] args) throws Exception {  
    3.         List<Integer> list = new ArrayList<>();  
    4.         for (int i = 0; i < 5; i++)  
    5.             list.add(i);  
    6.         // list {0, 1, 2, 3, 4}  
    7.         Iterator<Integer> it = list.iterator();  
    8.         while (it.hasNext()) {  
    9.             // index and number  
    10.             int num = it.next();  
    11.             System.out.print(num);  
    12.             if (num % 2 == 0) {  
    13.                 it.remove();  
    14.                 System.out.print(" delete");  
    15.             }  
    16.             System.out.println();  
    17.         }  
    18.     }  
    19. }  

    结果如下:
    iterator循环遍历
     
    可以看到顺利的执行了遍历并删除的操作,因此最推荐的做法是使用iterator执行遍历删除操作。
     
    以上是关于非线程安全的ArrayList,如果是线程安全的CopyOnWriteArrayList呢?
     

    使用普通for循环遍历

     
    [java] view plain copy
     
    1. public class Main {  
    2.     public static void main(String[] args) throws Exception {  
    3.         List<Integer> list = new CopyOnWriteArrayList<>();  
    4.         for (int i = 0; i < 5; i++)  
    5.             list.add(i);  
    6.         // list {0, 1, 2, 3, 4}  
    7.         for (int i = 0; i < list.size(); i++) {  
    8.             // index and number  
    9.             System.out.print(i + " " + list.get(i));  
    10.             if (list.get(i) % 2 == 0) {  
    11.                 list.remove(list.get(i));  
    12.                 System.out.print(" delete");  
    13.                 i--; // 索引改变!  
    14.             }  
    15.             System.out.println();  
    16.         }  
    17.     }  
    18. }  

    结果如下:
    CopyOnWriteArrayList遍历删除
    可以看到遍历删除是成功的,但是这种方法由于删除的时候会改变list的index索引和size大小,可能会在遍历时导致一些访问越界的问题,因此不是特别推荐。
     

    使用增强型for循环遍历

    [java] view plain copy
     
    1. public class Main {  
    2.     public static void main(String[] args) throws Exception {  
    3.         List<Integer> list = new CopyOnWriteArrayList<>();  
    4.         for (int i = 0; i < 5; i++)  
    5.             list.add(i);  
    6.         // list {0, 1, 2, 3, 4}  
    7.         for (Integer num : list) {  
    8.             // index and number  
    9.             System.out.print(num);  
    10.             if (num % 2 == 0) {  
    11.                 list.remove(num);  
    12.                 System.out.print(" delete");  
    13.             }  
    14.             System.out.println();  
    15.         }  
    16.     }  
    17. }  

    结果如下:
    CopyOnWriteArrayList增强for遍历删除
     
    可以看见与ArrayList遍历删除时情况不同,CopyOnWriteArrayList是允许使用增强型for进行循环遍历删除的。
     

    使用iterator遍历

    [java] view plain copy
     
    1. public class Main {  
    2.     public static void main(String[] args) throws Exception {  
    3.         List<Integer> list = new CopyOnWriteArrayList<>();  
    4.         for (int i = 0; i < 5; i++)  
    5.             list.add(i);  
    6.         // list {0, 1, 2, 3, 4}  
    7.         Iterator<Integer> it = list.iterator();  
    8.         while (it.hasNext()) {  
    9.             // index and number  
    10.             int num = it.next();  
    11.             System.out.print(num);  
    12.             if (num % 2 == 0) {  
    13.                 it.remove();  
    14.                 System.out.print(" delete");  
    15.             }  
    16.             System.out.println();  
    17.         }  
    18.     }  
    19. }  

    结果如下:
    CopyOnWriteArrayList的iterator遍历删除
     
    与ArrayList不同,由于CopyOnWriteArrayList的iterator是对其List的一个“快照”,因此是不可改变的,所以无法使用iterator遍历删除。
     
    综上所述,当使用ArrayList时,我们可以使用iterator实现遍历删除;而当我们使用CopyOnWriteArrayList时,我们直接使用增强型for循环遍历删除即可,此时使用iterator遍历删除反而会出现问题。
  • 相关阅读:
    mysql find_int_set
    PHPSTROM8.0 注册码(7.1也可用)
    gym 101657 D
    gym101657 C
    poj 3525
    poj1279
    poj3335
    poj 1228
    poj 1873
    poj 2074
  • 原文地址:https://www.cnblogs.com/KingIceMou/p/6976477.html
Copyright © 2020-2023  润新知