關於List比較好玩的操作
作為Java大家庭中的集合類框架,List應該是平時開發中最常用的,可能有這種需求,當集合中的某些元素符合一定條件時,想要刪除這個元素。如:
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循環優化,如:
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循環增強,不顯示的操作下標,直接操作對象,如:
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.AbstractList$Itr.checkForComodification(AbstractList.java:449)
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方法,該方法會刪除當前叠代對象的同時,維護索引的一致性。如:
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,如:
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]。
還有種取巧的方式是從最後一個元素開始遍歷,符合條件的刪除,如:
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的實現類對插入、刪除的效率不太一樣,這取決於其實現的數據結構,是選擇刪除,還是選擇新建個集合,這裏就不做討論了。
關於List比較好玩的操作