正確在遍歷中刪除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遍歷刪除反而會出現問題。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM