Deixe-me dar alguns exemplos com algumas alternativas para evitar a ConcurrentModificationException
.
Suponha que tenhamos a seguinte coleção de livros
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")));
Coletar e remover
A primeira técnica consiste em coletar todos os objetos que queremos excluir (por exemplo, usando um loop for aprimorado) e depois de concluir a iteração, removemos todos os objetos encontrados.
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);
Isso supõe que a operação que você deseja executar seja "excluir".
Se você deseja "adicionar", essa abordagem também funcionaria, mas eu presumiria que você iteraria sobre uma coleção diferente para determinar quais elementos você deseja adicionar a uma segunda coleção e, em seguida, emitiria um addAll
método no final.
Usando ListIterator
Se você estiver trabalhando com listas, outra técnica consiste em usar um ListIterator
que tenha suporte para remoção e adição de itens durante a própria iteração.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Novamente, usei o método "remove" no exemplo acima, que é o que sua pergunta parecia implicar, mas você também pode usar o add
método para adicionar novos elementos durante a iteração.
Usando JDK> = 8
Para aqueles que trabalham com o Java 8 ou versões superiores, existem algumas outras técnicas que você pode usar para tirar proveito disso.
Você pode usar o novo removeIf
método na Collection
classe base:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
Ou use a nova API de fluxo:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
Neste último caso, para filtrar elementos de uma coleção, você reatribui a referência original à coleção filtrada (ou seja books = filtered
) ou usou a coleção filtrada aos removeAll
elementos encontrados da coleção original (ou seja books.removeAll(filtered)
).
Usar sublista ou subconjunto
Existem outras alternativas também. Se a lista estiver classificada e você desejar remover elementos consecutivos, poderá criar uma sub-lista e desmarcá-la:
books.subList(0,5).clear();
Como a sub-lista é apoiada pela lista original, essa seria uma maneira eficiente de remover essa sub-coleção de elementos.
Algo semelhante poderia ser alcançado com conjuntos classificados usando o NavigableSet.subSet
método, ou qualquer um dos métodos de corte oferecidos lá.
Considerações:
Que método usado pode depender do que você pretende fazer
- A coleta e a
removeAl
técnica funcionam com qualquer coleção (coleção, lista, conjunto etc.).
- A
ListIterator
técnica, obviamente, só funciona com listas, desde que os seus dados ListIterator
ofertas de implementação apoio para acções adicionar e remover.
- A
Iterator
abordagem funcionaria com qualquer tipo de coleção, mas suporta apenas operações de remoção.
- Com a abordagem
ListIterator
/ Iterator
, a vantagem óbvia é não ter que copiar nada, pois removemos à medida que iteramos. Então, isso é muito eficiente.
- O exemplo de fluxos do JDK 8 na verdade não removeu nada, mas procurou os elementos desejados e, em seguida, substituímos a referência de coleção original pelo novo e deixamos o antigo ser coletado como lixo. Portanto, iteramos apenas uma vez sobre a coleção e isso seria eficiente.
- Na coleta e
removeAll
abordagem, a desvantagem é que precisamos iterar duas vezes. Primeiro, iteramos no loop foor procurando um objeto que atenda aos nossos critérios de remoção e, uma vez encontrado, pedimos para removê-lo da coleção original, o que implicaria um segundo trabalho de iteração para procurar esse item para remova.
- Eu acho que vale a pena mencionar que o método remove da
Iterator
interface está marcado como "opcional" nos Javadocs, o que significa que pode haver Iterator
implementações que serão lançadas UnsupportedOperationException
se invocarmos o método remove. Como tal, eu diria que essa abordagem é menos segura do que outras, se não podemos garantir o suporte do iterador para a remoção de elementos.