10
votes

Guava Multimap et ConcurrentModificationException

Je ne comprends pas pourquoi je reçois une idée concurrenteModification lorsque je iTère via ce multimap code>. Je lis ce qui suit entrée , mais je ne suis pas sûr de bien comprendre le tout. J'ai essayé d'ajouter un bloc synchronisé. Mais mon doute est de quoi se synchroniser et quand.

Le multimap code> est un champ et créé comme ceci: p> xxx pré>

et utilisé comme ceci: p>

for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) {
    if (entry.getValue().equals(command)) {
        eventMultiMap.remove(entry.getKey(), entry.getValue());
        nbRemoved++;
    }
}


5 Réponses :


11
votes

appeler Supprimer sur une collection pendant que vous itération à travers cela entraînera une exception concurrente à chaque fois, même si tout est fait dans le même thread - la bonne chose à faire est d'obtenir un itérateur explicite et d'appeler () à ce sujet. .

Edit: Modification de votre exemple: P>

Iterator<Map.Entry<GenericEvent, Command>> i = eventMultiMap.entries().iterator();
while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
}


3 commentaires

Ok, je reçois le point. Mais Itérateur ne peut prendre qu'un type de paramètre. Non ? Si j'étant itérale, les valeurs de ma carte et supprimez-les. La valeur correspondante sera-t-elle supprimée dans la carte d'origine (EventMultimap)?


OK, l'itérateur sur les valeurs de la carte peut supprimer les valeurs. Merci. Je ne peux pas accepter votre réponse pour l'instant, Itérateur n'est pas la compilation, mais le modifier et j'accepterai.


Désolé. Ne pas avoir Google-collections au travail, je ne pouvais donc pas tester le code avant de la publier. S'ils sont conçus comme le Hashmap Java, il devrait être possible d'utiliser un itérateur sur les objets d'entrée - j'ai édité de la montrer et de le télécharger quand je rentre à la maison, si vous n'avez pas eu la chance de l'essayer en attendant.



4
votes

Si un autre thread pourrait modifier votre multimap tandis que cette logique est en cours d'exécution, vous devez ajouter un bloc synchronisé au code de Mharris: xxx pré>

ou, vous pouvez omettre l'itérateur comme suit, P>

synchronized (eventMultimap) {
  int oldSize = eventMultimap.size();
  eventMultimap.values().removeAll(Collections.singleton(command));
  nbRemoved = oldSize - eventMultimap.size();
}


1 commentaires

J'aime le removeall (collection.Singleton (StufftToremove)). Merci.



5
votes

Vous voudrez peut-être voir Ce blogpost pour Un autre piège produisant un ConcurrentModificationException lors de la traversée d'un multimap, sans autre fil d'interférence. En bref, si vous parcourez les touches de Multimap, accédez à la collection correspondante de valeurs associées à chaque clé et supprimez un élément d'une telle collection, si cet élément se trouve être le dernier de la collection vous allez avoir concurrentModificationException lorsque vous essayez d'accéder à la touche suivante - car la vidange d'une collection déclenche la suppression de la clé, modifiant ainsi structurellement le clavier de Multimap.


1 commentaires

... Donc, le correctif pour cela serait de vérifier la taille de la valeur de la valeur pendant que vous l'itéritez, et si jamais cette valeur de valeur ne dispose que d'une entrée à gauche lorsque vous allez en retirer, il suffit de supprimer de l'itérateur du Keyset à la place.



1
votes

Je préfère multimap.values ​​(). Itérateur () Si vous ne vous souciez pas de la clé. Vous devriez également essayer de rester à l'écart de l'utilisation des blocs synchronisés autant que possible, car vous ne pouvez pas hiérarchiser efficacement. XXX


0 commentaires

1
votes

Dans Java8, vous pouvez également utiliser une approche Lambda:

EventMultimap.entries (). Remudidif (généreuseCommandEntry -> GenericeventCommandentry.getvalue (). Equals (commande));


0 commentaires