Por que não estou obtendo um java.util.ConcurrentModificationException neste exemplo?

Nota: Estou ciente do método Iterator#remove() .

No exemplo de código a seguir, não entendo por que o método List.remove in main lança ConcurrentModificationException , mas não no método remove .

 public class RemoveListElementDemo { private static final List integerList; static { integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer toRemove) { for(Integer integer : integerList) { if(integer.equals(toRemove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer toRemove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(toRemove)) { integerList.remove(integer); } } } } 

Aqui está o porquê: Como é dito no Javadoc:

Os iteradores retornados pelos methods iterator e listIterator dessa class são fail-fast: se a lista for estruturalmente modificada a qualquer momento após a criação do iterador, de qualquer forma, exceto pelos methods remove ou add do iterador, o iterador lançará um ConcurrentModificationException.

Essa verificação é feita no método next() do iterador (como você pode ver no stacktrace). Mas nós iremos alcançar o método next() somente se hasNext() entregado true, que é o que é chamado pelo para que cada um verifique se o limite é atingido. Em seu método remove, quando hasNext() verifica se ele precisa retornar outro elemento, ele verá que ele retornou dois elementos e, agora, depois que um elemento foi removido, a lista contém apenas dois elementos. Então tudo é peachy e terminamos com iteração. A verificação de modificações concorrentes não ocorre, pois isso é feito no método next() , que nunca é chamado.

Em seguida, chegamos ao segundo loop. Depois de removermos o segundo número, o método hasNext irá verificar novamente se pode retornar mais valores. Já retornou dois valores, mas a lista agora contém apenas um. Mas o código aqui é:

 public boolean hasNext() { return cursor != size(); } 

1! = 2, então continuamos no método next() , que agora percebe que alguém está mexendo na lista e triggers a exceção.

Espero que isso elimine sua dúvida.

Resumo

List.remove() não lançará ConcurrentModificationException quando remover o segundo último elemento da lista.

Uma maneira de lidar com isso é remover algo de uma cópia de uma Collection (não da própria coleção), se aplicável. Clone a coleção original para fazer uma cópia através de um Constructor .

Essa exceção pode ser lançada por methods que detectaram a modificação simultânea de um object quando tal modificação não é permitida.

Para o seu caso específico, em primeiro lugar, eu não acho que final é um caminho a percorrer, considerando que você pretende modificar a lista de declaração passado

 private static final List integerList; 

Considere também modificar uma cópia em vez da lista original.

 List copy = new ArrayList(integerList); for(Integer integer : integerList) { if(integer.equals(remove)) { copy.remove(integer); } } 

O método de encaminhamento / iterador não funciona ao remover itens. Você pode remover o elemento sem erro, mas receberá um erro de tempo de execução ao tentar acessar os itens removidos. Você não pode usar o iterador porque, como mostra o pushy, ele causará um ConcurrentModificationException, então use um loop for regular, mas passe por ele.

 List integerList; integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); int size= integerList.size(); //Item to remove Integer remove = Integer.valueOf(3); 

Uma solução:

Atravessar a matriz na ordem inversa, se você estiver indo para remover um elemento da lista. Simplesmente retrocedendo pela lista, você evita visitar um item que foi removido, o que remove a exceção.

 //To remove items from the list, start from the end and go backwards through the arrayList //This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error //for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator for (int i=size-1; i> -1; i--) { if (integerList.get(i).equals(remove) ) { integerList.remove(i); } } 

Este snippet sempre lançará um ConcurrentModificationException.

A regra é “Você não pode modificar (adicionar ou remover elementos da lista) enquanto iterar usando um Iterator (o que acontece quando você usa um loop for-each)”.

JavaDocs:

Os iteradores retornados pelos methods iterator e listIterator dessa class são fail-fast: se a lista for estruturalmente modificada a qualquer momento após a criação do iterador, de qualquer forma, exceto pelos methods remove ou add do iterador, o iterador lançará um ConcurrentModificationException.

Portanto, se você quiser modificar a lista (ou qualquer coleção em geral), use o iterador, porque ele está ciente das modificações e, portanto, elas serão tratadas corretamente.

Espero que isto ajude.

Eu tive esse mesmo problema, mas no caso que eu estava adicionando en elemento na lista iterada. Eu fiz desta maneira

 public static void remove(Integer remove) { for(int i=0; i 

Agora tudo corre bem porque você não cria nenhum iterador na sua lista, você itera sobre ele "manualmente". E condição i < integerList.size() nunca vai enganar você porque quando você remove / adiciona algo em tamanho de lista do decremento / incremento de lista ..

Espero que ajude, para mim isso foi solução.

Isso funciona bem no Java 1.6

~% javac RemoveListElementDemo.java
~% java RemoveListElementDemo
~% cat RemoveListElementDemo.java

 import java.util.*; public class RemoveListElementDemo { private static final List integerList; static { integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer remove) { for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer remove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } } 

~%

Se você usar collections copy-on-write, isso funcionará; no entanto, quando você usa list.iterator (), o Iterator retornado sempre referenciará a coleção de elementos como era quando (como abaixo) list.iterator () foi chamado, mesmo que outro thread modifique a coleção. Qualquer método de mutação chamado em um Iterator baseado em cópia-em-gravação ou ListIterator (como adicionar, definir ou remover) lançará uma UnsupportedOperationException.

 import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class RemoveListElementDemo { private static final List integerList; static { integerList = new CopyOnWriteArrayList<>(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer remove) { for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer remove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } } 

Mude o Iterator for each for loop para resolver.

E a razão é:

Os iteradores retornados pelos methods iterator e listIterator dessa class são fail-fast: se a lista for estruturalmente modificada a qualquer momento após a criação do iterador, de qualquer forma, exceto pelos methods remove ou add do iterador, o iterador lançará um ConcurrentModificationException.

– Java Docs referenciados.

No meu caso eu fiz assim:

 int cursor = 0; do { if (integer.equals(remove)) integerList.remove(cursor); else cursor++; } while (cursor != integerList.size()); 

Verifique seu código man ….

No método principal, você está tentando remover o 4º elemento que não está lá e, portanto, o erro. No método remove () você está tentando remover o terceiro elemento que está lá e, portanto, nenhum erro.