AFAIK, existem duas abordagens:
- Iterar uma cópia da coleção
- Use o iterador da coleção real
Por exemplo,
List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
// modify actual fooList
}
e
Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
// modify actual fooList using itr.remove()
}
Existem razões para preferir uma abordagem à outra (por exemplo, preferindo a primeira abordagem pelo simples motivo de legibilidade)?
java
collections
iteration
user1329572
fonte
fonte
while
tinha regras de escopo diferentes do quefor
fooList
é uma variável de instância e chamar um método durante o loop que acaba chamando outro método na mesma classe que o fazfooList.remove(obj)
. Já vi isso acontecer. Nesse caso, copiar a lista é mais seguro.Respostas:
Deixe-me dar alguns exemplos com algumas alternativas para evitar a
ConcurrentModificationException
.Suponha que tenhamos a seguinte coleção de livros
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.
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.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 naCollection
classe base:Ou use a nova API de fluxo:
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 aosremoveAll
elementos encontrados da coleção original (ou sejabooks.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:
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
removeAl
técnica funcionam com qualquer coleção (coleção, lista, conjunto etc.).ListIterator
técnica, obviamente, só funciona com listas, desde que os seus dadosListIterator
ofertas de implementação apoio para acções adicionar e remover.Iterator
abordagem funcionaria com qualquer tipo de coleção, mas suporta apenas operações de remoção.ListIterator
/Iterator
, a vantagem óbvia é não ter que copiar nada, pois removemos à medida que iteramos. Então, isso é muito eficiente.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.Iterator
interface está marcado como "opcional" nos Javadocs, o que significa que pode haverIterator
implementações que serão lançadasUnsupportedOperationException
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.fonte
removeAll(filtered)
. Um atalho para isso seriaremoveIf(b -> b.getIsbn().equals(other))
No Java 8, há outra abordagem. Coleção # removeIf
por exemplo:
fonte
A primeira abordagem funcionará, mas possui a sobrecarga óbvia de copiar a lista.
A segunda abordagem não funcionará porque muitos contêineres não permitem modificações durante a iteração. Isso inclui
ArrayList
.Se a única modificação é remover o elemento atual, você pode fazer o segundo trabalho de abordagem usando
itr.remove()
(ou seja, usar o iterador 'sremove()
método, não o recipiente ' s). Esse seria o meu método preferido para iteradores compatíveisremove()
.fonte
Iterator
interface está marcado como opcional nos Javadocs, o que significa que pode haver implementações do Iterator que possam ser lançadasUnsupportedOperationException
. Como tal, eu diria que essa abordagem é menos segura que a primeira. Dependendo das implementações que se pretende usar, a primeira abordagem pode ser mais adequada.remove()
na própria coleção original também pode lançarUnsupportedOperationException
: docs.oracle.com/javase/7/docs/api/java/util/… . Infelizmente, as interfaces de contêiner Java são definidas como extremamente não confiáveis (derrotando honestamente o ponto da interface). Se você não souber a implementação exata que será usada no tempo de execução, é melhor fazer as coisas de maneira imutável - por exemplo, use a API Java 8+ Streams para filtrar os elementos e coletá-los em um novo contêiner. substituir completamente o antigo por ele.Apenas a segunda abordagem funcionará. Você pode modificar a coleção durante a iteração usando
iterator.remove()
apenas. Todas as outras tentativas causarãoConcurrentModificationException
.fonte
Favorito antigo do temporizador (ainda funciona):
fonte
Você não pode fazer o segundo, porque mesmo se você usar o
remove()
método no Iterator , receberá uma exceção .Pessoalmente, eu preferiria o primeiro para todas as
Collection
instâncias, apesar do escutar adicional de criar o novoCollection
, acho menos propenso a erros durante a edição por outros desenvolvedores. Em algumas implementações de coleção, o Iteratorremove()
é suportado; em outros, não. Você pode ler mais nos documentos do Iterator .A terceira alternativa é criar uma nova
Collection
iteração sobre o original e adicionar todos os membros do primeiroCollection
ao segundoCollection
que não estão prontos para exclusão. Dependendo do tamanhoCollection
e do número de exclusões, isso pode economizar significativamente na memória, quando comparado à primeira abordagem.fonte
Eu escolheria o segundo, pois você não precisa fazer uma cópia da memória e o Iterator funciona mais rápido. Então você economiza memória e tempo.
fonte
por que não isso?
E se for um mapa, não uma lista, você pode usar keyset ()
fonte
get(i)
você precisará visitar todos os nós até chegari
.Foo.remove(i);
você deve fazeri--;
?