2013-07-10 12 views
5

ho creato un nuovo ArrayList utilizzando sottolista Method.Now quando si tenta di eseguire l'operazione di intersezione utilizzando retainAll getta seguente eccezionePerché retainAll in ArrayList genera un'eccezione

retainAll() metodo funziona per sottostante Codice

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); 

ma quando provo ad applicare retainAll per codice qui sotto genera eccezioni come qui sotto

codice 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 {} 

ERRORE

enter image description here

+2

Ove possibile, un esempio di codice senza dipendenze sconosciute è il migliore. Ad esempio, questo si verifica solo con la classe Fruit o una classe Java standard ha lo stesso problema? –

+0

Quando l'elenco contiene un numero, diciamo quando creo un elenco retainAll funziona gr8 –

+0

Sì, ho usato String e testato ha funzionato anche –

risposta

3

Nei due esempi di codice si ha la lista grande e la sottolista in ordine inverso.

Quando si invoca retainAll() nell'elenco secondario, non si verificheranno modifiche.

Questo perché ogni elemento nell'elenco secondario è nella lista grande.

Se non si verifica alcuna modifica, viene emesso il numero ConcurrentModificationException.

Fai questo sopra con l'elenco di Numeri interi.


Se si inverte l'ordine e richiamare retainAll() sul grande lista, otterrà mutato.

Questo perché non tutti gli elementi nella grande lista si trovano nella sotto-lista.

Quando si rimuove un elemento dalla lista grande, viene lanciato uno ConcurrentModificationException.

Questo perché non è possibile modificare la lista mentre si sta eseguendo l'iterazione su.

Fai questo sopra con l'elenco di Frutta.


L'iterazione avviene nel metodo retainAll().

Nel tuo codice, l'argomento lista si riferisce alla stessa lista che viene modificata.

Questo è a causa del modo List.subList() opere:

restituisce una vista della porzione di questa lista tra il fromIndex specificato, inclusivo, e toIndex, esclusivo. (Se fromIndex e toIndex sono uguali, l'elenco restituito è vuoto.) L'elenco restituito è supportato da questo elenco, quindi le modifiche non strutturali nell'elenco restituito si riflettono in questo elenco e viceversa.


Per farla breve:

Non si ottiene un'eccezione se si modifica il codice a questo:

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

Ancora più importante:

È necessario creare una nuova lista dalla sotto-lista se c'è la possibilità che entrambe le liste vengano modificate mentre uno degli elenchi viene iterato.

È possibile creare un nuovo elenco dal sub-lista come questa:

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

Ora è possibile scorrere e mutare al contenuto del vostro cuore!


Ecco un'implementazione di ArrayList.retainAll(), dove si può guardare per l'iterazione.

+1

+1 Volevo dire la stessa cosa, ma non ho avuto la pazienza di scriverlo. – NINCOMPOOP

+0

Anche il codice in 'contains()', il confronto con 'size' può essere una ragione probabile:' for (int i = 0; i NINCOMPOOP

+0

Probabilmente è lo stesso caso quando si tenta di rimuovere elementi da 'List' usando' List # remove() 'durante l'iterazione. In alcuni casi limite funziona. – NINCOMPOOP

7

Quando si utilizza List#subList():

Restituisce una vista della porzione di questa lista tra il fromIndex specificato, inclusivo, e toIndex, esclusivo. (Se fromIndex e toIndex sono uguali, l'elenco restituito è vuoto.) L'elenco restituito è supportato da questo elenco, quindi le modifiche non strutturali nell'elenco restituito si riflettono in questo elenco e viceversa. L'elenco restituito supporta tutte le operazioni di elenco facoltativo supportate da questo elenco.

È consentito modificare elementi all'interno di esso ma non modificare la struttura dell'elenco.

Il dottore dice inoltre:

La semantica della lista restituita da questo metodo diventa indefinito se l'elenco di supporto (cioè, l'elenco) è strutturalmente modificato in alcun modo se non attraverso l'elenco restituito. (modifiche strutturali sono quelli che cambiano le dimensioni di questo elenco, o altrimenti perturbare in modo tale che iterazioni in corso possono produrre risultati errati.)

La funzione retainAll() utilizza un iteratore per eliminare i valori non intersecanti , questo causa ConcurrentModificationException. Nota ciò che il documenation dice:

Si noti che questa eccezione non sempre indica che un oggetto è stato contemporaneamente modificato da un thread diverso. Se un singolo thread genera una sequenza di invocazioni di metodi che violano il contratto di un oggetto, l'oggetto può lanciare questa eccezione.

Effettuare una copia del List e quindi eseguire retainAll():

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

Ho scritto una risposta importante su come la mutazione durante l'iterazione è stata la causa dell'eccezione ... Poi ho aggiunto un collegamento a un'implementazione OpenJdk di [' retainAll() '] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.retainAll%28java.util. collezione% 29). Guardando il codice, l'iterazione non è così ovvia come pensavo sarebbe. Il ciclo for è sufficiente per generare l'eccezione o è causato da qualcos'altro (forse la chiamata a 'System.arrayCopy()')? – jahroy

+0

Ho visto il codice sembra 'System.arrayCopy()' è il colpevole. – NINCOMPOOP

2

Il problema è che arrNewFruits è in realtà solo una vista logica di una parte di arrFruits. Per evitare l'errore, è necessario fare una lista indipendente:

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

Questo è il motivo per cui è possibile rimuovere parte di una lista chiamando clear() su un subList() — modifiche a uno sono visti in altro.

+1

Sono convinto della tua risposta, ma perché funziona quando creo ArrayList di Integer –

+1

@JavaBeginner - Non ** lavorerà usando Integer se invochi 'retainAll()' nella lista grande (invece del sotto- elenco). Guarda il tuo codice, i due esempi non sono per niente uguali! Invochi 'retainAll()' nella ** piccola ** lista di Interi, e invochi 'retainAll()' nella ** grande ** lista di Frutti. – jahroy

Problemi correlati