List--遍歷時的錯誤用法

       作爲Java大家庭中的集合類框架,List應該是平時開發中最常用的,可能有這種需求,當集合中的某些元素符合一定條件時,想要刪除這個元素。如:

  1. public class ListTest {  
  2.     public static void main(String[] args) {  
  3.        List<Integer> intList = new ArrayList<Integer>();  
  4.        Collections.addAll(intList, 12356);  
  5.        // for循環優化寫法,只獲取一次長度  
  6.        for(int i = 0, size = intList.size(); i < size; i++) {  
  7.            Integer value = intList.get(i);  
  8.            // 符合條件,刪除元素  
  9.            if(value == 3 || value == 5) {  
  10.               intList.remove(i);  
  11.            }  
  12.        }  
  13.        System.out.println(intList);  
  14.     }  
  15. }  
public class ListTest {
    public static void main(String[] args) {
       List<Integer> intList = new ArrayList<Integer>();
       Collections.addAll(intList, 1, 2, 3, 5, 6);
       // for循環優化寫法,只獲取一次長度
       for(int i = 0, size = intList.size(); i < size; i++) {
           Integer value = intList.get(i);
           // 符合條件,刪除元素
           if(value == 3 || value == 5) {
              intList.remove(i);
           }
       }
       System.out.println(intList);
    }
}
      執行後,會拋出IndexOutOfBoundsException,因爲集合中存在符合條件的元素,刪除後,集合長度動態改變,由於長度只獲取一次,發生越界,所以,去掉for循環優化,如:
  1. public class ListTest {  
  2.     public static void main(String[] args) {  
  3.        List<Integer> intList = new ArrayList<Integer>();  
  4.        Collections.addAll(intList, 12356);  
  5.        for(int i = 0; i < intList.size(); i++) {  
  6.            Integer value = intList.get(i);  
  7.            // 符合條件,刪除元素  
  8.            if(value == 3 || value == 5) {  
  9.               intList.remove(i);  
  10.            }  
  11.        }  
  12.        System.out.println(intList);  
  13.     }  
  14. }  
public class ListTest {
    public static void main(String[] args) {
       List<Integer> intList = new ArrayList<Integer>();
       Collections.addAll(intList, 1, 2, 3, 5, 6);
       for(int i = 0; i < intList.size(); i++) {
           Integer value = intList.get(i);
           // 符合條件,刪除元素
           if(value == 3 || value == 5) {
              intList.remove(i);
           }
       }
       System.out.println(intList);
    }
}
      輸出:[1, 2, 5, 6],漏掉了5這個元素,當i=2的時候,值爲3,刪除後,後面的元素往前補一位,這時i=3的時候,值爲6,跳過了5,這樣也不行,隨後想到了用for循環增強,不顯示的操作下標,直接操作對象,如:
  1. public class ListTest {  
  2.     public static void main(String[] args) {  
  3.        List<Integer> intList = new ArrayList<Integer>();  
  4.        Collections.addAll(intList, 12356);  
  5.        for(Integer value : intList) {  
  6.            // 符合條件,刪除元素  
  7.            if(value == 3 || value == 5) {  
  8.               intList.remove(value);  
  9.            }  
  10.        }  
  11.        System.out.println(intList);  
  12.     }  
  13. }  
public class ListTest {
    public static void main(String[] args) {
       List<Integer> intList = new ArrayList<Integer>();
       Collections.addAll(intList, 1, 2, 3, 5, 6);
       for(Integer value : intList) {
           // 符合條件,刪除元素
           if(value == 3 || value == 5) {
              intList.remove(value);
           }
       }
       System.out.println(intList);
    }
}
      執行後,會拋出ConcurrentModificationException,字面意思是併發修改異常。異常跟蹤信息如下:

Exception inthread “main” java.util.ConcurrentModificationException

         atjava.util.AbstractListItr.checkForComodification(AbstractList.java:449)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at java.util.AbstractList Itr.next(AbstractList.java:420)

         at ListTest.main(ListTest.java:13)

       可以大概看出是執行到AbstractList中內部類Itr的checkForComodification方法拋出的異常,至於爲什麼出現異常,這裏可以大概解釋一下。集合遍歷是使用Iterator, Iterator是工作在一個獨立的線程中,並且擁有一個互斥鎖。Iterator 被創建之後會建立一個指向原來對象的單鏈索引表,當原來的對象數量發生變化時,這個索引表的內容不會同步改變,所以當索引指針往後移動的時候就找不到要迭代的對象,所以按照 fail-fast原則 Iterator 會馬上拋出java.util.ConcurrentModificationException 異常。所以 Iterator 在工作的時候是不允許被迭代的對象被改變的。

       而要解決這個問題,可以使用Iterator的remove方法,該方法會刪除當前迭代對象的同時,維護索引的一致性。如:

  1. public class ListTest {  
  2.     public static void main(String[] args) {  
  3.        List<Integer> intList = new ArrayList<Integer>();  
  4.        Collections.addAll(intList, 12356);  
  5.        Iterator<Integer> it = intList.iterator();  
  6.        while(it.hasNext()) {  
  7.            Integer value = it.next();  
  8.            if(value == 3 || value == 5) {  
  9.               it.remove();  
  10.            }  
  11.        }  
  12.        System.out.println(intList);  
  13.     }  
  14. }  
public class ListTest {
    public static void main(String[] args) {
       List<Integer> intList = new ArrayList<Integer>();
       Collections.addAll(intList, 1, 2, 3, 5, 6);
       Iterator<Integer> it = intList.iterator();
       while(it.hasNext()) {
           Integer value = it.next();
           if(value == 3 || value == 5) {
              it.remove();
           }
       }
       System.out.println(intList);
    }
}
      輸出正確結果:[1, 2, 6]。

       不使用迭代器的解決方案就是,自己維護索引,刪除一個元素後,索引-1,如:

  1. public class ListTest {  
  2.     public static void main(String[] args) {  
  3.        List<Integer> intList = new ArrayList<Integer>();  
  4.        Collections.addAll(intList, 12356);  
  5.        for(int i = 0; i < intList.size(); i++) {  
  6.            Integer value = intList.get(i);  
  7.            if(value == 3 || value == 5) {  
  8.               intList.remove(i);  
  9.               i–;  
  10.            }  
  11.        }  
  12.         System.out.println(intList);  
  13.     }  
  14. }  
public class ListTest {
    public static void main(String[] args) {
       List<Integer> intList = new ArrayList<Integer>();
       Collections.addAll(intList, 1, 2, 3, 5, 6);
       for(int i = 0; i < intList.size(); i++) {
           Integer value = intList.get(i);
           if(value == 3 || value == 5) {
              intList.remove(i);
              i--;
           }
       }
        System.out.println(intList);
    }
}
      輸出正確結果:[1, 2, 6]。

       還有種取巧的方式是從最後一個元素開始遍歷,符合條件的刪除,如:

  1. public class ListTest {  
  2.     public static void main(String[] args) {  
  3.        List<Integer> intList = new ArrayList<Integer>();  
  4.        Collections.addAll(intList, 12356);  
  5.        for(int i = intList.size() - 1; i >= 0; i–) {  
  6.            Integer value = intList.get(i);  
  7.            if(value == 3 || value == 5) {  
  8.               intList.remove(i);  
  9.            }  
  10.        }  
  11.         System.out.println(intList);  
  12.     }  
  13. }  
public class ListTest {
    public static void main(String[] args) {
       List<Integer> intList = new ArrayList<Integer>();
       Collections.addAll(intList, 1, 2, 3, 5, 6);
       for(int i = intList.size() - 1; i >= 0; i--) {
           Integer value = intList.get(i);
           if(value == 3 || value == 5) {
              intList.remove(i);
           }
       }
        System.out.println(intList);
    }
}
      輸出正確結果:[1, 2, 6]。

       最後,Java集合類框架真是大大方便了開發,不用自己去維護數組,隨時擔心着越界等問題。當然List的實現類對插入、刪除的效率不太一樣,這取決於其實現的數據結構,是選擇刪除,還是選擇新建個集合,這裏就不做討論了。

       本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/9347357

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章