Funzionamento iteratori

di il
57 risposte

57 Risposte - Pagina 4

  • Re: Funzionamento iteratori

    Per quanto riguarda le eccezioni che devono lanciare gli iteratori:
    non mi è chiaro il significato di ConcurrentModificationException e come va inserita nell'implementazione.
    
    	public Iterator<Integer> iterator() {
    		return new MyIter();
    	}
    	
    	private class MyIter implements Iterator<Integer> {
    		
    		private int index = 0;
    	   
    		private int find(int i) {
    			while(i < arr.length) { 
    				if(arr[i] == true) {
    					return i; 
    				}   
    				i++;
    	      	}
    			return -1;
    		}
    		
    		public boolean hasNext() { 
    			return (find(index) != -1);
    		}
    	       
    		public Integer next() {
    			index = find(index);
    			if(index == -1) throw new NoSuchElementException();
    			return index++;
    		}  
    	        
    		public void remove() { //non viene implementato
    			throw new UnsupportedOperationException();
    		}
    	   
    	}
    
  • Re: Funzionamento iteratori

    Paolinos ha scritto:


    non mi è chiaro il significato di ConcurrentModificationException e come va inserita nell'implementazione.
    È un po' lungo da spiegare ..... spiego domani mattina.
  • Re: Funzionamento iteratori

    Grazie mille
  • Re: Funzionamento iteratori

    Paolinos ha scritto:


    non mi è chiaro il significato di ConcurrentModificationException e come va inserita nell'implementazione.
    Prendiamo la classica iterazione:
    Iterator<Xyz> iter = unaCollezione.iterator();
    
    while (iter.hasNext()) {
        Xyz xyz = iter.next();
        // ......
    }
    Dal momento in cui si ottiene l'iteratore al momento in cui si arriva all'ultimo elemento può passare del tempo. Dipende ovviamente dal numero di elementi e da cosa fa il codice nel ciclo.
    Se in questo frangente di tempo si va a modificare strutturalmente la collezione (add, set, remove, ecc...), sia all'interno del ciclo stesso, sia da parte ad esempio di un altro thread che ha visibilità della collezione, COSA dovrebbe succedere? O per meglio dire: come dovrebbe comportarsi l'iteratore da quel momento in avanti?
    L'iteratore può continuare a iterare con successo? Può rischiare di "perdersi" degli elementi? Può causare altri problemi o danni alla struttura della collezione? La risposta è: DIPENDE da quale è il concetto della collezione e da come è implementata esattamente.

    Le collezioni "base" del framework, quelle direttamente in java.util es. ArrayList, Vector, HashMap forniscono un iteratore che ha un comportamento che si dice "fail-fast". Se durante la iterazione si modifica strutturalmente la collezione in qualunque modo ECCETTO attraverso il remove() del Iterator, allora l'iteratore lancia ConcurrentModificationException.

    Come si può fare questo? La implementazione di Oracle è abbastanza semplice: queste collezioni hanno un campo che fa da "contatore" delle modifiche (nei sorgenti Oracle si chiama modCount). Ad ogni modifica, con add, set, remove, ecc... questo contatore viene semplicemente incrementato.

    Quando vai ad ottenere l'Iterator, inizialmente si copia il valore del modCount in suo campo interno. Ogni volta che sul Iterator fai un next() o remove(), l'iteratore va a verificare se il suo contatore è ancora uguale a quello della collezione. Se non lo è lancia ConcurrentModificationException.
    Il senso del ConcurrentModificationException è quindi solamente quello di segnalare che la collezione è stata modificata durante la iterazione e quindi la iterazione in teoria non dovrebbe più continuare perché ci potrebbero essere problemi.

    Il termine "Concurrent" nel nome della eccezione non deve far pensare per forza al multi-threading. La collezione può essere modificata anche dentro quel ciclo while, quindi nello stesso thread che sta iterando!

    Il comportamento fail-fast comunque non è garantito al 100%. Per un motivo molto semplice: per evitare di aggiungere overhead, quindi diminuire le prestazioni, quel modCount viene incrementato senza usare alcun meccanismo di sincronizzazione. Questo ha una implicazione importante: se un thread A sta iterando e un thread B modifica la collezione, il thread A potrebbe anche non "vedere" (subito o mai) la modifica del modCount e quindi non lancerebbe ConcurrentModificationException.

    Alla fin fine, come dice la documentazione: ConcurrentModificationException should be used only to detect bugs.
    Ovvero se sbuca fuori ConcurrentModificationException dovrebbe denotare un baco poiché il codice non è stato fatto correttamente perché il programmatore ha permesso volutamente o per svista che la collezione venga modificata durante la iterazione.

    Valuta tu se in questa tua esercitazione è importante o no implementare un comportamento fail-fast nell'iteratore.
  • Re: Funzionamento iteratori

    Grazie del chiarimento
    Valuta tu se in questa tua esercitazione è importante o no implementare un comportamento fail-fast nell'iteratore.
    Viene richiesto che gli iteratori debbano lanciare in modo corretto ConcurrentModificationException (oltre NoSuchElementException)
  • Re: Funzionamento iteratori

    Paolinos ha scritto:


    Viene richiesto che gli iteratori debbano lanciare in modo corretto ConcurrentModificationException (oltre NoSuchElementException)
    Allora implementa il comportamento fail-fast come detto. Ma deve essere garantito al 100% oppure no? Come detto, per le collezioni base del framework NON è garantito al 100% in uno scenario multi-thread perché è fatto senza alcuna sincronizzazione.
  • Re: Funzionamento iteratori

    Ma deve essere garantito al 100% oppure no?
    Non è specificato
  • Re: Funzionamento iteratori

    Paolinos ha scritto:


    Non è specificato
    Allora puoi farlo proprio come quello del framework.
  • Re: Funzionamento iteratori

    Ricapitolando modCount è la variabile contatore che nel mio caso viene incrementata quando l'array viene modificato. Prima di creare l'iteratore copio il valore di modCount in un altro campo (per es. c=modCount).
    Dopo l'operazione di next() verifico se modCount!=c, se vero lancia l'eccezione.

    Quindi in BoolSet posso inserire:
    private int modCount = 0;
    il metodo
    
    final void verificaEccezione() {
            if (modCount != c)
                throw new ConcurrentModificationException();
    }
    
    Quando ridimensiono l'array faccio modCount++

    e per es nel metodo inter():
    
    	public void inter(Ins<Integer> k) {
    		int c=modCount;
    		Iterator<Integer> myIterator = new MyIter();
    		while(myIterator.hasNext()) {
    	         int temp=myIterator.next();
    				verificaEccezione();
    				if(!k.contains(temp))
    					arr[temp]=false;
    	    }		
    	}
    
  • Re: Funzionamento iteratori

    Paolinos ha scritto:


    Ricapitolando modCount è la variabile contatore che nel mio caso viene incrementata quando l'array viene modificato.
    Sì, questo è nella "collezione" (nel tuo caso BoolSet)

    Paolinos ha scritto:


    Prima di creare l'iteratore copio il valore di modCount in un altro campo (per es. c=modCount).
    Sì ma questa variabile "c" (o come vuoi chiamarla) va messa nella classe che implementa Iterator (NON direttamente nella collezione; BoolSet). Ciascun oggetto iteratore deve tenersi in pancia il valore del contatore attuale di quel momento, di quando viene creato l'iteratore.

    Paolinos ha scritto:


    Dopo l'operazione di next() verifico se modCount!=c, se vero lancia l'eccezione.
    Il confronto dei due contatori, quello della collezione con quello dell'iteratore, va fatto nell'iteratore stesso (next() e se lo implementi anche remove() ). NON va fatto nella collezione o comunque dove USI l'iteratore.
    E' l'iteratore che deve "fallire" se la collezione è stata modificata nel frattempo .... non il resto del codice.
  • Re: Funzionamento iteratori

    Ho dichiarato la variabile in BoolSet
    private int modCount=0;
    nella classe MyIter:
    
    private int c;
    ...
    public Integer next() {
    			c=modCount;
    			index = find(index);
    			if(c!=modCount) throw new ConcurrentModificationException();
    			if(index == -1) throw new NoSuchElementException();
    			return index++;
    		}
    
    Ad ogni modifica dell'array
    modCount++;
  • Re: Funzionamento iteratori

    Paolinos ha scritto:


    nella classe MyIter:
    
    private int c;
    ...
    public Integer next() {
    			c=modCount;
    			index = find(index);
    			if(c!=modCount) throw new ConcurrentModificationException();
    			if(index == -1) throw new NoSuchElementException();
    			return index++;
    		}
    
    No, non va bene. next NON deve cambiare alcun contatore. Il contatore dell'iteratore va settato come "istantanea" dalla collezione solo nel momento in cui viene creato l'iteratore.

    Quindi banalmente

    private int c = modCount;

    ed è già inizializzato corretto quando crei l'oggetto iteratore.

    Il remove deve o non deve aggiornare il contatore dell'iteratore a seconda se sfrutta o no metodi della collezione. Se ad esempio il remove() dell'iteratore andasse a sfruttare un remove della collezione (che aggiorna modCount), allora dovrebbe poi anche aggiornare c altrimenti il conteggio non sarebbe più "sincronizzato".
  • Re: Funzionamento iteratori

    Ok, ho capito.
    Grazie ancora!

    P.S.: fino a questo momento cercavo materiale online e mi muovevo tra varie dispense, discussioni ecc, ma adesso sto pensando (non è mai troppo tardi) di comprare una buona guida che copra tutti gli argomenti in modo chiaro, per questo mi sarebbe utile un consiglio. Grazie.
Devi accedere o registrarti per scrivere nel forum
57 risposte