Permettez-moi de donner quelques exemples avec quelques alternatives pour éviter a ConcurrentModificationException
.
Supposons que nous ayons la collection de livres suivante
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
Collecter et supprimer
La première technique consiste à collecter tous les objets que nous voulons supprimer (par exemple en utilisant une boucle for améliorée) et après avoir terminé l'itération, nous supprimons tous les objets trouvés.
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
Cela suppose que l'opération que vous souhaitez effectuer est "supprimer".
Si vous souhaitez "ajouter" cette approche fonctionnerait également, mais je suppose que vous parcourrez une autre collection pour déterminer les éléments que vous souhaitez ajouter à une deuxième collection, puis émettez une addAll
méthode à la fin.
Utilisation de ListIterator
Si vous travaillez avec des listes, une autre technique consiste à utiliser une ListIterator
qui prend en charge la suppression et l'ajout d'éléments lors de l'itération elle-même.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Encore une fois, j'ai utilisé la méthode "remove" dans l'exemple ci-dessus, ce que votre question semblait impliquer, mais vous pouvez également utiliser sa add
méthode pour ajouter de nouveaux éléments pendant l'itération.
Utilisation de JDK> = 8
Pour ceux qui travaillent avec Java 8 ou des versions supérieures, il existe quelques autres techniques que vous pouvez utiliser pour en tirer parti.
Vous pouvez utiliser la nouvelle removeIf
méthode dans la Collection
classe de base:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
Ou utilisez la nouvelle API de flux:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
Dans ce dernier cas, pour filtrer les éléments d'une collection, vous réaffectez la référence d'origine à la collection filtrée (c'est-à-dire books = filtered
) ou vous avez utilisé la collection filtrée pour removeAll
les éléments trouvés de la collection d'origine (c'est-à-dire books.removeAll(filtered)
).
Utiliser une sous-liste ou un sous-ensemble
Il existe également d'autres alternatives. Si la liste est triée et que vous souhaitez supprimer des éléments consécutifs, vous pouvez créer une sous-liste, puis la supprimer:
books.subList(0,5).clear();
Étant donné que la sous-liste est appuyée par la liste d'origine, ce serait un moyen efficace de supprimer cette sous-collection d'éléments.
Quelque chose de similaire pourrait être réalisé avec des ensembles triés à l'aide de la NavigableSet.subSet
méthode ou de l'une des méthodes de découpage proposées.
Considérations:
La méthode que vous utilisez peut dépendre de ce que vous avez l'intention de faire
- La collecte et la
removeAl
technique fonctionnent avec n'importe quelle collection (collection, liste, ensemble, etc.).
- La
ListIterator
technique ne fonctionne évidemment qu'avec des listes, à condition que leur ListIterator
implémentation donnée prenne en charge les opérations d'ajout et de suppression.
- L'
Iterator
approche fonctionnerait avec n'importe quel type de collection, mais elle ne prend en charge que les opérations de suppression.
- Avec l' approche
ListIterator
/ Iterator
, l'avantage évident est de ne pas avoir à copier quoi que ce soit puisque nous supprimons au fur et à mesure de l'itération. Donc, c'est très efficace.
- L'exemple de flux JDK 8 ne supprime en fait rien, mais a recherché les éléments souhaités, puis nous avons remplacé la référence de collection d'origine par la nouvelle et laissé l'ancienne être récupérée. Donc, nous itérons une seule fois sur la collection et ce serait efficace.
- Dans la collecte et l'
removeAll
approche, l'inconvénient est que nous devons répéter deux fois. D'abord, nous parcourons dans la boucle du plancher à la recherche d'un objet qui correspond à nos critères de suppression, et une fois que nous l'avons trouvé, nous demandons de le supprimer de la collection d'origine, ce qui impliquerait un deuxième travail d'itération pour rechercher cet article afin de le retirer.
- Je pense qu'il convient de mentionner que la méthode remove de l'
Iterator
interface est marquée comme "facultative" dans Javadocs, ce qui signifie qu'il pourrait y avoir des Iterator
implémentations qui jettent UnsupportedOperationException
si nous invoquons la méthode remove. En tant que tel, je dirais que cette approche est moins sûre que d'autres si nous ne pouvons pas garantir le support de l'itérateur pour la suppression d'éléments.