2013-07-10 25 views
5

我創建使用子列表Method.Now當我嘗試使用中的retainAll執行交集操作它拋出異常爲什麼在的retainAll ArrayList中拋出

中的retainAll(以下新的ArrayList)的例外方法適用於下面的代碼

List<Integer> arrNums1 = new ArrayList<Integer>(); 
arrNums1.add(1); 
arrNums1.add(2); 
arrNums1.add(3); 

List<Integer> arrNums2 = arrNums1.subList(0, 1); 
arrNums2.retainAll(arrNums1); 

但是當我嘗試申請中的retainAll下面的代碼會生成異常,如下

Java代碼的

public class Generics1 
{ 
public static void main(String[] args) 
{ 
     List<Fruits> arrFruits = new ArrayList<Fruits>(); 

     Fruits objApple = new Apple(); 
     Fruits objOrange = new Orange(); 
     Fruits objMango = new Mango(); 

     arrFruits.add(objApple); 
     arrFruits.add(objOrange); 
     arrFruits.add(objMango); 

     List<Fruits> arrNewFruits = arrFruits.subList(0, 1); 

     System.out.println(arrFruits.retainAll(arrNewFruits)); 
    } 
} 

class Fruits {} 

class Apple extends Fruits {} 

class Orange extends Fruits {} 

class Mango extends Fruits {} 

錯誤

enter image description here

+2

在可能的情況下,沒有未知關係的代碼示例是最好的。例如,這隻會在Fruit類中出現,還是會出現標準的Java類有相同的問題? –

+0

當列表包含一個數字說當我創建一個列表 retainAll作品gr8 –

+0

是的我使用了字符串,並測試它也工作 –

回答

3

在你的兩個代碼示例中,你有相反的順序大列表和子列表。

當調用子列表上retainAll(),也不會發生改變。

這是因爲子列表中的每個元素都在大列表中。

如果沒有發生改變,沒有ConcurrentModificationException將被拋出。

你與你的整數列表做到這一點上面。


如果顛倒順序和大名單上調用retainAll(),它會被突變。

這是因爲不是大列表中的每個項目都在子列表中。

當你從大名單中刪除一個元素,一個ConcurrentModificationException被拋出。

這是因爲你不能在迭代它的同時改變列表

你與你的水果列表做到這一點上面。


迭代發生在retainAll()方法中。

在你的代碼,列表參數恰好引用真實被修改的同一列表。

這是由於的方式List.subList()作品:

返回指定的fromIndex(包括)元素範圍,獨家之間的這份名單的一部分的視圖。 (如果fromIndex和toIndex相等,則返回的列表爲空。)返回的列表由此列表支持,因此返回列表中的非結構化更改將反映在此列表中,反之亦然。


長話短說:

System.out.println(arrNewFruits.retainAll(arrFruits)); 

更重要的是:

如果你改變你的代碼,這樣你就不會得到一個異常

如果有一個機會,其中一個列表正在迭代時,您可能需要從子列表中創建一個新列表。

您可以從子列表這樣創建一個新的列表:

List<Foo> freshList = new ArrayList<Foo>(bigList.subList(0,2)); 

現在,您可以遍歷併發生變異,你的心臟的內容!


下面是ArrayList.retainAll()的實現,您可以在其中查找迭代。

+1

+1我想說同樣的話,但沒有耐心去寫。 – NINCOMPOOP

+0

甚至在'contains()'中的代碼,檢查'size'也可能是一個可能的原因:'for(int i = 0; i NINCOMPOOP

+0

當您嘗試在迭代時使用'List#remove()'從List列表中移除元素時,可能會出現同樣的情況。在一些邊緣情況下,它的作品。 – NINCOMPOOP

7

當您使用List#subList()

返回指定的fromIndex(包括)元素範圍之間的這個名單的部分視圖,獨家。(如果fromIndex和toIndex相等,則返回的列表爲空。)返回的列表由此列表支持,因此返回列表中的非結構化更改將反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可選列表操作。

您可以對其中的元素進行變異,但不能更改列表的結構。

的DOC進一步說:

通過此方法返回的列表語義變爲不確定如果支持列表(即,該列表)在結構上在比通過返回的列表的其他任何形式的修改。(結構修改是指改變該列表的大小,或者以其他方式干擾它以這樣的方式,在正在進行的迭代產生不正確的結果。)

retainAll()函數使用一個迭代刪除不相交的值,這會導致ConcurrentModificationException。注意documenation說什麼:

請注意,此異常不會始終指出對象已經由不同線程併發修改。如果單個線程發出違反對象合約的一系列方法調用,則該對象可能會拋出此異常。

充分利用List,然後副本進行retainAll()

List<Fruits> arrNewFruits = new ArrayList<>(arrFruits.subList(0, 1)); 
+0

我寫了一個關於如何在迭代過程中導致異常的重要答案......然後我添加了一個OpenJdk實現的鏈接[''的retainAll()'](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.retainAll%28java.util。收集29%)。看代碼,迭代並不像我想象的那麼明顯。 for循環足以拋出異常,還是由其他內容引起(可能是調用'System.arrayCopy()')? – jahroy

+0

我看到代碼似乎'System.arrayCopy()'是罪魁禍首。 – NINCOMPOOP

2

的問題是,arrNewFruits實際上是arrFruits一部分只是一個邏輯視圖。要避免錯誤,你需要做一個獨立的列表:

List<Fruits> arrNewFruits = new ArrayList<>(arrFruits.subList(0, 1)); 

這就是爲什麼你可以通過調用一個subList() —改變一個clear()刪除列表中的一部分,是出現在其他。

+1

我相信你的答案,但爲什麼它的工作原理當我創建整數的ArrayList –

+1

@JavaBeginner - 它不會**工作使用整數,如果你在大列表上調用retainAll()'(而不是子列表)列表)。看看你的代碼,這兩個例子根本不一樣!您可以在**小**整數列表中調用'retainAll()',但是您可以在**大**列表中調用'retainAll()'。 – jahroy

相關問題