Como remover um elemento? - Collections Framework e Iterator

Fala galera!

Este tópico é sobre remover elementos em uma estrutura do framework Collection e sobre a classe Iterator.

Estou estudando sobre Collections e brincando um pouco com o código cheguei ao seguinte problema:

No código abaixo, tenho 3 loops (1, 2 e 3 comentado).

O dois primeiros loops são loops do tipo enhanced-for. O loop 1 se comporta como eu esperava porém o loop 2, não.

Eu espera remover todas as cores que começam com a letra “B”, mas é lançado uma exception.

Só consegui fazer a remoção usando um Iterator, conforme loop 3.

Quero entender por que o Java se comporta de forma diferente do loop 1 no loop2 se a lógica e estrutura do código são muito similares.

Código:

public class TestCollections {
    public static void main(String[] args) {
        Set<String> colors = new HashSet<>();
        colors.add("Blue");
        colors.add("Red");
        colors.add("Purple");
        colors.add("Yellow");
        colors.add("Black");

        System.out.println(colors);
        colors.remove("Red");
        System.out.println(colors);

        colors.add("Brown");

        //1
        for (String color : colors) {
            if (color.startsWith("B")) {
                System.out.println(color);
            }
        }

        //2
        for (String color : colors) {
            if (color.startsWith("B")) {
                colors.remove(color);
            }
        }

        //3
        Iterator<String> iterator = colors.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().startsWith("B")) iterator.remove();
        }
        System.out.println(colors);
    }
}

Exception:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1511)
	at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1534)
	at diocollection.sets.exe.TestCollections.main(TestCollections.java:30)

Basicamente, você não pode remover um elemento de uma coleção no mesmo loop que itera sobre ela.

Por que? Bem, porque a linguagem foi definida assim :slight_smile:

Inclusive, a documentação diz que a maneira correta de fazer isso é usando o Iterator. Por isso o loop 2 não funciona e o 3 sim.


Se estiver usando Java >= 8, também pode usar removeIf:

colors.removeIf(c -> c.startsWith("B"));
2 curtidas

Porque no contador o loop precisa ser percorrido até o fim, pois o número de itens é conhecido de antemão. No iterator, cada item da lista tem um ponteiro para o próximo item, quando não tiver (hasNext) é porque ele é o último item da lista.
Essa exceção ConcurrentModificationException é uma proteção criada propositalmente contra bugs e outros problemas. Não é permitido modificar uma coleção enquanto ele está sendo percorrido.

1 curtida