Iterazione su una lista

di il
4 risposte

Iterazione su una lista

Ciao, sto cercando di capire quali sono i metodi corretti per iterare su una lista e modificarla contestualmente.

Mettiamo, per esempio, che io voglia convertire tutti gli elementi di una lista in maiuscolo utilizzando il metodo
private static void convertToUppercaseStrings(List<String> list1)
Utilizzando un ListIterator, potrei scrivere:
private static void convertToUppercaseStrings(List<String> list1) {
	ListIterator <String> iterator = list1.listIterator();
	while(iterator.hasNext()){
		iterator.set(iterator.next().toUpperCase());
	}
}
NON posso, invece, modificare la lista attraverso un for-each
(for Element e : list)
, come in

private static void convertToUppercaseStrings(List<String> list1) {
	int i = 0;
	for(String s : list1){
		String upperCase = s.toUpperCase();
		list1.remove(s);
		list1.add(i, upperCase);
	}
}
In questo caso, il metodo lancia una java.util.ConcurrentModificationException.

Il metodo con il for "standard" cioè
private static void convertToUppercaseStrings(List<String> list1) {
	for(int i = 0; i < list1.size(); i++){
 		String upperCase = list1.get(i).toUpperCase();
		list1.remove(i);
		list1.add(i, upperCase);
	}
}
no dà problemi di compilazione e produce un autput corretto.
Il metodo quindi può essere utilizzato?

L'iteratore deve essere NECESSARIAMENTE utilizzato solo quando si usa un for-each?

4 Risposte

  • Re: Iterazione su una lista

    eclipto ha scritto:


    NON posso, invece, modificare la lista attraverso un for-each
    (for Element e : list)
    , come in
    
    private static void convertToUppercaseStrings(List<String> list1) {
    	int i = 0;
    	for(String s : list1){
    		String upperCase = s.toUpperCase();
    		list1.remove(s);
    		list1.add(i, upperCase);
    	}
    }
    No, per 2 motivi:

    1) Usando il for-each l'iteratore è "nascosto", non lo puoi usare (e quindi nemmeno invocare remove() ).
    2) Gli iteratori delle collezioni standard hanno un comportamento che è definito "fail-fast". Se l'iteratore scopre che la collezione è stata modificata strutturalmente con qualunque metodo che NON è tra quelli offerti da Iterator (solo remove, in pratica), lancia ConcurrentModificationException.

    Con il for-each potresti modificare lo "stato" dell'oggetto i-esimo, se fosse MUTABILE. Ma String è immutabile, quindi con il for-each NON puoi fare quanto detto, ovvero portare in maiuscolo le stringhe modificando la STESSA lista.

    eclipto ha scritto:


    Il metodo con il for "standard" cioè
    private static void convertToUppercaseStrings(List<String> list1) {
    	for(int i = 0; i < list1.size(); i++){
     		String upperCase = list1.get(i).toUpperCase();
    		list1.remove(i);
    		list1.add(i, upperCase);
    	}
    }
    no dà problemi di compilazione e produce un autput corretto.
    Il metodo quindi può essere utilizzato?
    A parte che NON serve remove+add (inutile/fumoso). Basta fare un set. Ma a parte questo, l'accesso con get/set per indice è performante o meno a seconda del tipo di collezione. Con ArrayList è performante, perché ArrayList è basato su un array ma con LinkedList sarebbe molto poco performante, proporzionalmente alla lunghezza della lista!

    eclipto ha scritto:


    L'iteratore deve essere NECESSARIAMENTE utilizzato solo quando si usa un for-each?
    Non è chiara la domanda. il for-each su qualunque oggetto che è-un Iterable USA un Iterator.
  • Re: Iterazione su una lista

    andbin ha scritto:


    Con il for-each potresti modificare lo "stato" dell'oggetto i-esimo, se fosse MUTABILE. Ma String è immutabile, quindi con il for-each NON puoi fare quanto detto, ovvero portare in maiuscolo le stringhe modificando la STESSA lista.
    Mi potresti fare un esempio?

    andbin ha scritto:


    NON serve remove+add (inutile/fumoso). Basta fare un set.
    Sì, my bad, era lì perché non sapevo come funzionasse add quando l'ho scritto (pensavo in pratica facesse quello che fa set e quindi andava in loop infinito). Ora lo riscriverei con set.

    andbin ha scritto:


    Ma a parte questo, l'accesso con get/set per indice è performante o meno a seconda del tipo di collezione. Con ArrayList è performante, perché ArrayList è basato su un array ma con LinkedList sarebbe molto poco performante, proporzionalmente alla lunghezza della lista!
    Ok, quindi in pratica dipende dall'implementazione della lista. Con LinkedList, per esempio, la prima soluzione è la migliore.

    andbin ha scritto:


    eclipto ha scritto:


    L'iteratore deve essere NECESSARIAMENTE utilizzato solo quando si usa un for-each?
    Non è chiara la domanda. il for-each su qualunque oggetto che è-un Iterable USA un Iterator.
    Mi sono espressa malissimo.
    Utilizzando il for-each, come mi hai detto, i problemi sono due:
    1 - l'iteratore è nascosto
    2 - è impossibile modificare la lista con metodi della lista e non dell'iteratore
    Questi sono preblemi che mi devo aspettare esclusivamente quando utilizzo un for-each?
    Sto guardando adesso le Collection e l'uso degli iteratori, vorrei capire quali sono le cose che non posso assolutamente fare se non voglio una runtime exception o un errore.

    Grazie per la risposta velocissima, comunque
  • Re: Iterazione su una lista

    eclipto ha scritto:


    Mi potresti fare un esempio?
    Di cosa? Con oggetti mutabili?
    Immagina di avere una classe Punto2D "mutabile", ovvero ha i getX/getY ma anche setX/setY. Se avessi un List<Punto2D> puoi usare il for-each e siccome puoi modificare lo stato degli oggetti con i setter, potresti ad esempio spostare tutti i punti di un tot. Senza creare nuovi oggetti o una nuova lista.

    In sostanza, con il for-each NON puoi cambiare i riferimenti contenuti nella lista, ovvero non puoi assegnare agli elementi della lista.

    eclipto ha scritto:


    Ok, quindi in pratica dipende dall'implementazione della lista. Con LinkedList, per esempio, la prima soluzione è la migliore.
    Se puoi usare un ListIterator (e non solo un Iterator), sì, è una buona soluzione se devi per forza cambiare la lista e non crearne una nuova.

    eclipto ha scritto:


    Mi sono espressa malissimo.
    Utilizzando il for-each, come mi hai detto, i problemi sono due:
    1 - l'iteratore è nascosto
    2 - è impossibile modificare la lista con metodi della lista e non dell'iteratore
    Questi sono preblemi che mi devo aspettare esclusivamente quando utilizzo un for-each?
    Sì sono queste. Il for-each è sostanzialmente quello che si dice "zucchero sintattico", ovvero è solo una sintassi abbreviata che genera del codice che è di fatto uguale a quello che potresti tranquillamente scrivere anche tu. Non c'è niente di "nuovo".
    Ma il fatto è che nasconde delle cose (l'iteratore se su Iterable o l'indice di iterazione se su un array), quindi ci sono dei limiti.

    Il for-each NON va bene SE:
    a) Vuoi aggiungere o rimuovere elementi in una collezione.
    b) Vuoi cambiare i riferimenti contenuti in una collezione o i valori/riferimenti contenuti in un array.
    c) Vuoi iterare in "parallelo" su due collezioni o array.
  • Re: Iterazione su una lista

    Perfetto, grazie mille!
Devi accedere o registrarti per scrivere nel forum
4 risposte