web-dev-qa-db-fra.com

Utiliser l'itérateur sur un TreeSet

SITUATION: J'ai un TreeSet d'objets personnalisés et j'ai également utilisé un comparateur personnalisé. J'ai créé un itérateur à utiliser sur cet TreeSet.

TreeSet<Custom> ts=new TreeSet<Custom>();
Iterator<Custom> itr=ts.iterator();
while(itr.hasNext()){
    Custom c=itr.next();
    //Code to add a new element to the TreeSet ts
}

QUESTION: Eh bien, je veux savoir que si j'ajoute un nouvel élément au TreeSet dans la boucle while, ce nouvel élément sera immédiatement trié. En d’autres termes, si j’ajoute un nouvel élément dans la boucle while et qu’il est inférieur à celui que je tiens actuellement dans c, lors de la prochaine itération, le même élément sera-t-il identique à celui de la dernière itération? après le tri, l’élément nouvellement ajouté occupera une place quelque part avant l’élément actuel).

9
aps

Si vous ajoutez un élément lors de votre itération, votre prochain appel d'itérateur émettra probablement une ConcurrentModificationException. Voir le comportement d'échec rapide dans TreeSet docs.

Pour itérer et ajouter des éléments, vous pouvez d'abord copier dans un autre jeu:

TreeSet<Custom> ts = ...
TreeSet<Custom> tsWithExtra = new TreeSet(ts);

for (Custom c : ts) {
  // possibly add to tsWithExtra
}

// continue, using tsWithExtra

ou créez une collection séparée à fusionner avec ts après itération, comme le suggère Colin.

23

Vous obtiendrez une Java.util.ConcurrentModificationException si vous ajoutez un élément dans la boucle TreeSet à l'intérieur de la boucle while.

Set<String> ts=new TreeSet<String>();
ts.addAll(Arrays.asList(new String[]{"abb", "abd", "abg"}));
Iterator<String> itr=ts.iterator();
while(itr.hasNext()){
    String s = itr.next();
    System.out.println("s: " + s);
    if (s.equals("abd"))
        ts.add("abc");
}

Sortie

Exception in thread "main" Java.util.ConcurrentModificationException
6
anubhava
public static void main(String[] args) {
    TreeSet<Integer> ts=new TreeSet<Integer>();
    ts.add(2);
    ts.add(4);
    ts.add(0);

    Iterator<Integer> itr=ts.iterator();
    while(itr.hasNext()){
        Integer c=itr.next();
        System.out.println(c);
        //Code
        ts.add(1);
    }
}


Exception in thread "main" Java.util.ConcurrentModificationException

Cela arrivera à toutes les collections comme List, Map, Set car quand iterator démarre, il se peut qu'il soit verrouillé.

si vous parcourez une liste à l'aide d'un itérateur, cette exception surviendra. Je pense que sinon cette boucle sera infinie au fur et à mesure que vous ajouterez un élément entier itérant.

Considérez sans itérateur:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>();
    list.add(2);
    list.add(4);
    list.add(0);

    for (int i = 0; i < 3; i++) {
        System.out.println(list.get(i));
        list.add(3);
    }
    System.out.println("Size" +list.size());
}

ce sera bien.

3
NILESH SALPE

Afin d'éviter la ConcurrentModificationException, vous pouvez consulter mon UpdateableTreeSet . J'ai même ajouté un nouveau cas de test montrant comment ajouter des éléments au cours d'une boucle. Pour être plus précis, vous marquez de nouveaux éléments pour une mise à jour différée ultérieure de l'ensemble. Cela fonctionne assez bien. Fondamentalement, vous faites quelque chose comme

for (MyComparableElement element : myUpdateableTreeSet) {
    if (someCondition) {
        // Add new element (deferred)
        myUpdateableTreeSet.markForUpdate(
            new MyComparableElement("foo", "bar", 1, 2)
        );
    }
}

// Perform bulk update
myUpdateableTreeSet.updateMarked();

Je suppose que c'est exactement ce dont vous avez besoin. :-)

1
kriegaex

Alors que la question a déjà été répondue, je pense que la réponse la plus satisfaisante réside dans javadoc de TreeSet lui-même

Les itérateurs renvoyés par la méthode itérateur de cette classe ont la capacité d'échec rapide: si l'ensemble est modifié à tout moment après sa création, de quelque manière que ce soit, excepté par le biais de la méthode remove de ce dernier, ce dernier lancera une exception ConcurrentModificationException. Ainsi, face aux modifications simultanées, l'itérateur échoue rapidement et proprement, au lieu de risquer un comportement non déterministe arbitraire à une date future indéterminée.

Notez que le comportement de défaillance d'un itérateur ne peut pas être garanti car il est en général impossible de donner des garanties fermes en présence de modifications simultanées non synchronisées. Les itérateurs qui échouent rapidement lancent l'exception ConcurrentModificationException du mieux possible. Par conséquent, il serait erroné d’écrire un programme dont l’exactitude est fonction de cette exception: le comportement à la suite d’une défaillance rapide des itérateurs ne devrait être utilisé que pour détecter des bogues.

0
KNU

Pour éviter l'erreur de modification simultanée inévitable lors de l'insertion, vous pouvez également créer une copie temporaire de l'ensemble, parcourir la copie à la place et modifier l'original.

0
ragerdl

Pour empêcher l'exception ConcurrentModificationException en marchant. Ci-dessous ma version pour permettre l'insertion de hautes fréquences dans TreeSet () et permettre simultanément de l'itérer dessus. Cette classe utilise une file d'attente supplémentaire pour stocker l'objet d'insertion lorsque le TreeSet est en cours d'itération.

public class UpdatableTransactionSet {
TreeSet <DepKey> transactions = new TreeSet <DepKey> ();
LinkedList <DepKey> queue = new LinkedList <DepKey> ();
boolean busy=false;
/**
 * directly call it
 * @param e
 */
void add(DepKey e) {
    boolean bb = getLock();
    if(bb) {
        transactions.add(e);
        freeLock();
    } else {
        synchronized(queue) {
            queue.add(e);
        }
    }
}
/**
 * must getLock() and freeLock() while call this getIterator function
 * @return
 */
Iterator<DepKey> getIterator() {
    return null;
}

synchronized boolean getLock() {
    if(busy) return false;
    busy = true;
    return true;
}
synchronized void freeLock() {
    synchronized(queue) {
        for(DepKey e:queue) {
            transactions.add(e);
        }
    }       
    busy = false;
}
}
0
Houcheng