2012-01-05 103 views
3

以下函數通過列表遞歸遍歷並將其總分爲一半,並對子列表執行一些操作。當列表大小爲2時,遞歸會中斷。我知道如果在迭代它時更改列表,會發生併發修改異常。但我不使用迭代和它仍然發生:Java列表和遞歸導致併發修改異常

private static List<ParticipantSlot> divide(List<ParticipantSlot> list) { 
     int n = list.size(); 

     //do something 

     if (n>2){ 
      List<ParticipantSlot> l = divide(list.subList(0, n/2-1)); 
      List<ParticipantSlot> r= divide(list.subList(n/2, n)); 

      l.addAll(r); 
      return l; 
     }else{ 
      return list; 
     } 
    } 
+1

如果列表支持完整列表,subList不會生成列表的副本。 – 2012-01-05 15:11:16

回答

7

您使用addAll()它會遍歷你的論點提供集合。現在subList只返回一個視圖到原來的列表,所以你要添加值到原始列表的視圖,並在同一時間遍歷原始列表的不同部分。砰。

如果您每次創建子列表的副本,它應該可以工作 - 儘管它效率很低。

+0

謝謝,這最後解釋了它。 – 2012-01-05 17:20:22

3

你得到併發修改例外,因爲子列表是由原始列表支持:

返回的列表受此列表的支持,因此返回列表中的非結構性變化反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可選列表操作。

如果您想避免例外情況,請在修改第一個子列表之前製作副本。

+0

不過,究竟如何引發併發修改異常呢? – 2012-01-05 15:54:51

+0

@MikeNakis調用'l.addAll(r)'通過調用'sublist'獲得的「視圖」間接修改'list'。 – dasblinkenlight 2012-01-05 15:59:56

+0

請原諒,如果我有一個金髮的時刻,但...同時修改它什麼?我沒有看到任何迭代或其他事情同時發生在列表上。或者你是否在說,按照設計,如果列表中的任何修改都會導致「併發修改」異常,如果一個子列表已經被創建出來而不是GCd呢?這可能有點奇怪,因爲這意味着一旦我有了一個子列表,我永遠不會修改原始列表,因爲我無法保證子列表何時或者是否會被GCd。 – 2012-01-05 16:06:03