什麼是 fail-fast 機制?
fail-fast機制在遍歷一個集合時,當集合結構被修改,會拋出Concurrent Modification Exception。 fail-fast會在以下兩種情況下拋出ConcurrentModificationException
(1)單線程環境 集合被創建後,在遍歷它的過程中修改了結構。
注意 remove()方法會讓expectModcount和modcount 相等,所以是不會拋出這個異常。
(2)多線程環境 當一個線程在遍歷這個集合,而另一個線程對這個集合的結構進行了修改。
如下:
private static void test0() { List<String> list = new ArrayList<>(); list.add("a1"); list.add("a2"); list.add("a3"); list.add("a4"); list.add("a5"); list.add("a6"); list.add("a7"); list.add("a8"); for (String s : list) { list.remove(s); } System.out.println(list); }
運行會拋出異常
java.util.ConcurrentModificationException
如果又想在遍歷的時候做刪除操作呢? 借肋iterator,如下:
private static void test1() { List<String> list = new ArrayList<>(); list.add("a1"); list.add("a2"); list.add("a3"); list.add("a4"); list.add("a5"); list.add("a6"); list.add("a7"); list.add("a8"); Iterator<String> iter = list.iterator(); while (iter.hasNext()) { String str = iter.next(); System.out.println(str); //iter.remove(); if (Randoms.random(1, 9) % 2 == 0) { iter.remove(); } } System.out.println(list); }
這裡我們並沒有用list.remove元素而是iter.reomve,但實際list中的元素已經被刪除也沒有拋異常,為什麼會這樣?
調試的時候我們發現iter.reomve執行最終調用的是 ArrayList
public E remove(int index)
所以自然影響了原來的集合。
Iterator被創建之後會建立一個指向原來對象的單鏈索引表, 當list刪除元素裡不會影響索引,Iterator.remove() 方法會在刪除當前迭代對象的同時維護索引的一致性。
我們再看段代碼:
private static void test2() { List list2 = Arrays.asList(new String[]{"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8",}); Iterator iter2 = list2.iterator(); while (iter2.hasNext()) { String str = iter2.next(); System.out.println(str); //iter.remove(); if (Randoms.random(1, 9) % 2 == 0) { iter2.remove(); } } System.out.println(list2); }
結果:
會拋出異常java.lang.UnsupportedOperationException
為什麼呢?感覺跟test1是一樣的?
我們查看源碼Arrays:
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); } /** * @serial include */ private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable{...}
發現兩個ArrayList並不是同一個類卻取了同一個名字:
java.util.Arrays.ArrayList!=java.util.ArrayList
你還是你同一個名字卻是另一個他....
java.util.Arrays.ArrayList並沒有實現remove方法,所以也就拋出了UnsupportedOperationException 就不足為其了!
看似簡單的問題多想一點就好了!