2013-10-22 56 views
1

實際上,我學習收藏和例外,我不明白爲什麼這個工程:雖然Collections.shuffle()不會拋出ConcurrentModificationException

List<Integer> intList = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9,10)); 
for (Integer s : intList) { 
    Collections.shuffle(intList); 
    System.out.println(s); 
} 

閱讀該文檔,它指出

當這樣的修改不被允許時,這種異常可能會被檢測到併發修改對象的方法拋出。

綜觀集合源代碼:

public static void shuffle(List<?> list) { 
     if (r == null) { 
      r = new Random(); 
     } 
     shuffle(list, r); 
} 

所以我來看看隨機播放功能:

public static void shuffle(List<?> list, Random rnd) { 
     int size = list.size(); 
     if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { 
      for (int i=size; i>1; i--) 
       swap(list, i-1, rnd.nextInt(i)); 
     } else { 
      Object arr[] = list.toArray(); 

      // Shuffle array 
      for (int i=size; i>1; i--) 
       swap(arr, i-1, rnd.nextInt(i)); 

      // Dump array back into list 
      ListIterator it = list.listIterator(); 
      for (int i=0; i<arr.length; i++) { 
       it.next(); 
       it.set(arr[i]); 
      } 
     } 
    } 

最後,調用交換功能:

public static void swap(List<?> list, int i, int j) { 
     final List l = list; 
     l.set(i, l.set(j, l.get(i))); 
} 

這不會修改當前列表,而itera (或這是因爲這條線final List l = list;)?我想我錯過了一些重要的東西。

+2

我想在這裏修改意味着添加(或)刪除哪些改變列表的大小,而不是交換。 – kosa

+0

它可能是特定於實現的,但'set'通常不會更改運行整個ConcurrentModificationException的'modCount' show .. –

回答

6

答案在於documentation - 重點礦山:

(結構上的修改是指添加或刪除一個或多個元件,或明確地調整大小背襯陣列的任何操作; 僅僅設置的值元素是不是結構修改

...

的迭代器通過此類的iterator和方法的ListIterator返回的快速失敗的:如果列表在迭代器創建後的任何時候,除了通過迭代器自己的remove或add方法以外的任何方式都會在結構上修改,迭代器將拋出ConcurrentModificationException。因此,面對併發修改,迭代器快速而乾淨地失敗,而不是在將來未定的時間冒着任意的,非確定性的行爲冒險。

shuffle只要求set,因此它不執行結構性改動,因此迭代器不拋出異常。

+0

「'shuffle'只調用'set'」 - 在這個特定的實現中。 – Holger

+0

@霍爾:是的,的確如此。我認爲這將是一個不尋常的實現,而不是僅僅執行掉期。 –

3
for (int i=size; i>1; i--) 
    swap(list, i-1, rnd.nextInt(i)); 

這不是迭代在這裏,本身;沒有涉及Iterator。而在第二個分支中,它確實執行了所有修改Iterator,這是您應該如何避免ConcurrentModificationException的做法。

相關問題