2013-02-26 22 views
11

devo inferiore a quella di due javaL'elenco genera ConcurrentModificationException ma set non lancia ConcurrentModificationException?

import java.util.*; 

public class ArrayListTest032 { 
    public static void main(String[] ar) { 
     List<String> list = new ArrayList<String>(); 
     list.add("core java"); 
     list.add("php"); 
     list.add("j2ee"); 
     list.add("struts"); 
     list.add("hibernate"); 

     Iterator<String> itr = list.iterator(); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 
     list.remove("php"); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 

    } 
} 

Quando eseguo sopra codice ottengo sotto uscita.

core java 
php 
j2ee 
struts 
hibernate 

Exception in thread "main" java.util.ConcurrentModificationException 
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) 
    at java.util.AbstractList$Itr.next(AbstractList.java:343) 
    at ArrayListTest032.main(ArrayListTest032.java:20) 

Quale è previsto mentre sto modificando l'elenco durante l'iterazione. Ma in sotto la classe java la stessa logica viene eseguita dalla famiglia set.

import java.util.*; 

public class HashSetTest021 { 
    public static void main(String[] ar) { 
     Set<String> set = new HashSet<String>(); 
     set.add("core java"); 
     set.add("php"); 
     set.add("j2ee"); 
     set.add("struts"); 
     set.add("hibernate"); 

     Iterator<String> itr = set.iterator(); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 
     set.remove("php"); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 

    } 
} 

E in uscita è.

hibernate 
core java 
j2ee 
php 
struts 

Non c'è ConcurrentModificationException qualsiasi.

Voglio solo sapere perché stesso pezzo di codice genera ConcurrentModificationException in caso di list famiglia, ma non c'è alcuna ConcurrentModificationException in caso di set famiglia

risposta

4

Questa è una differenza nell'implementazione: l'iteratore restituito dall'elenco di array rileva modifiche simultanee anche quando è posizionato alla fine, perché controlla la lunghezza; gli iteratori di HashSet, TreeSet e LinkedList, d'altro canto, non rilevano questa condizione, perché controllano che siano posizionati alla fine prima di verificare la modifica concomitante. La documentazione consente agli iteratori di non apportare modifiche simultanee, quindi entrambi gli approcci sono validi.

+0

+1 per demo fiddle –

+0

nit: l'ArrayList iterator controlla effettivamente un conteggio delle modifiche, non la lunghezza di per sé, quindi se si aggiunge un elemento immediatamente rimosso, lasciando la lunghezza uguale, si otterrà comunque una ConcurrentModificationException in seguito. –

1
public static void main(String[] ar) { 
      List<String> list = new ArrayList<String>(); 
      list.add("core java"); 
      list.add("php"); 
      list.add("j2ee"); 
      list.add("struts"); 
      list.add("hibernate"); 

      Iterator<String> itr = list.iterator(); 

      while (itr.hasNext()) { 
       System.out.println(itr.next()); 
      } 
      list.remove("php"); 

      /* while (itr.hasNext()) { 
       System.out.println(itr.next()); 
      }*/ 

     } 

problem in itr object.it holds the list object reference 
+0

quindi perché set non genera eccezioni. puoi controllare entrambe le classi –

+0

Dalla traccia dello stack di output, è chiaro che l'eccezione verrà quando chiameremo la funzione iterator next(). Se ti stai chiedendo come Iterator controlli la modifica, la sua implementazione è presente nella classe AbstractList dove è definito un modCount variabile int che fornisce il numero di volte che la dimensione dell'elenco è stata modificata. – Biswajit

5

Si tratta di una sorta di ' comportamento retrogrado, in quanto gli iteratori, una volta attraversati completamente, non sono riutilizzabili, ovvero il loro metodo hasNext deve restituire false quando si raggiunge la fine dell'elenco.

In questo caso, però, l'iteratore restituito da ArrayList.iterator è una classe di implementazione interna, con il codice per hasNext come segue:

public boolean hasNext() { 
    return cursor != size; 
} 

Così, quando si chiama hasNext nel secondo ciclo, indica (falsamente) che ci sono più elementi da iterare, perché hai eseguito un'operazione che ha cambiato la dimensione della lista, dopo la prima iterazione. Semanticamente, non dovresti essere in grado di continuare a scorrere gli elementi nell'elenco dopo aver raggiunto la fine di esso, ma a causa di questa implementazione dettagli ti permette di procedere con il secondo ciclo while. Naturalmente, a quel punto, si ottiene un'eccezione di modifica simultanea a causa della modifica apportata nell'elenco di supporto.

D'altra parte, l'iteratore utilizzata dal set hash ha il hasNext implementata come segue:

public final boolean hasNext() { 
    return next != null; 
} 

Questa implementazione non sembra essere come 'vulnerabile' modifiche apportate al hash impostare dopo un'iterazione è stato completato e, come tale, il metodo hasNext si comporta meglio.

+0

Ok e in caso di set famiglia? –

+0

@Real - dettagli aggiunti per l'implementazione del set di hash. – Perception

2

Iniziare leggendo il JavaDoc per Iterator. Si parla di ConcurrentModificationException ovunque?

Ora, leggere il JavaDoc per ConcurrentModificationException, e notare quanto segue (il corsivo):

Questa eccezione può essere gettato con metodi che hanno rilevato modifica simultanea di un oggetto quando tale modifica non è consentito.

Ora guarda attentamente il tuo codice. Il tuo ciclo while itera su tutti gli elementi della collezione (anche se l'output del tuo primo esempio non lo indica, il che mi dice che hai modificato l'output o che questo non è il tuo codice reale). Nel momento in cui rimuovi l'elemento, non ci sono più elementi da iterare, quindi il secondo ciclo dovrebbe sempre uscire immediatamente.

Quindi, la conclusione è che gli esecutori della lista iteratore hanno scelto che a gettare che fa eccezione, anche quando non ci sono più elementi per iterare, mentre gli esecutori del iteratore insieme hanno scelto di non farlo. Entrambi i casi sono completamente accettabili date le specifiche.

+0

Ho aggiunto la stessa classe nel codice sopra è possibile eseguire il codice e controllare l'output da soli. –

+0

@Real - sì, l'ho eseguito, e l'output ha mostrato "ibernazione", che il tuo post non ha. Ma davvero, piuttosto che ottenere i tuoi cortometraggi su un commento genetico, dovresti pensare al * contenuto * della mia risposta. – parsifal

+0

Qualche idea del motivo per cui questo design in cui la lista getta e imposta non è stato preferito? – djechlin

1

Hashset può lanciare un ConcurrentModificationException se si fa qualcosa sul set tranne attraverso l'iteratore. Tuttavia, ci sono molte euristiche attorno al comportamento di fast-fail di itertor con l'obiettivo di completare l'iterazione se possibile. I JavaDocs sembrano abbastanza chiari sul suo comportamento.

0

In caso di lista quando si attraversa con il primo ciclo Iterator itr = set.iterator();

while (itr.hasNext()) { 
     System.out.println(itr.next()); 
    } 

Il valore del cursore e la dimensione diventerà same.Cursor contiene valore totale senza elementi muovere e all'interno hashNext() metodo per la lista di attraversamento contiene codice come:

public boolean hasNext() { 
      return cursor != size; 
     } 

Così dopo primo ciclo while cursor == size. Dopo aver rimosso l'elemento dalla dimensione dell'elenco diventa (originalSize-1). Quindi per il ciclo while while va dentro mentre e all'interno del metodo itr.next() controlla la modifica del modcount e lancia ConcurrentModificationException.

In caso di Set controlla per next! restituisce next == null come true e quindi non entra nel ciclo while per controllare la modifica del modcount. Quindi non lancia Eccezione ConcurrentModification.

Problemi correlati