最近在寫代碼的時候遇到了遍歷時刪除List元素的問題,在此寫一篇博客記錄一下。
一般而言,遍歷List元素有以下三種方式:
- 使用普通for循環遍歷
- 使用增強型for循環遍歷
- 使用iterator遍歷
使用普通for循環遍歷
代碼如下:
- 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();
- }
- }
- }
結果如下:

可以看到遍歷刪除偶數的結果是成功的,但是這種方法由於刪除的時候會改變list的index索引和size大小,可能會在遍歷時導致一些訪問越界的問題,因此不是特別推薦。
使用增強型for循環遍歷
- 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();
- }
- }
- }
結果如下:

可以看到刪除第一個元素時是沒有問題的,但刪除后繼續執行遍歷過程的話就會拋出ConcurrentModificationException的異常。
使用iterator遍歷
- 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();
- }
- }
- }
結果如下:

可以看到順利的執行了遍歷並刪除的操作,因此最推薦的做法是使用iterator執行遍歷刪除操作。
以上是關於非線程安全的ArrayList,如果是線程安全的CopyOnWriteArrayList呢?
- 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();
- }
- }
- }
結果如下:

可以看到遍歷刪除是成功的,但是這種方法由於刪除的時候會改變list的index索引和size大小,可能會在遍歷時導致一些訪問越界的問題,因此不是特別推薦。
使用增強型for循環遍歷
- 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();
- }
- }
- }
結果如下:

可以看見與ArrayList遍歷刪除時情況不同,CopyOnWriteArrayList是允許使用增強型for進行循環遍歷刪除的。
使用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不同,由於CopyOnWriteArrayList的iterator是對其List的一個“快照”,因此是不可改變的,所以無法使用iterator遍歷刪除。
綜上所述,當使用ArrayList時,我們可以使用iterator實現遍歷刪除;而當我們使用CopyOnWriteArrayList時,我們直接使用增強型for循環遍歷刪除即可,此時使用iterator遍歷刪除反而會出現問題。