Menu

Iterando coleções em Java: for-each, Iterator, forEach

As formas de percorrer coleções em Java - o laço for-each, o Iterator, laços com índice e o método forEach - e como remover elementos com segurança durante a iteração.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um trabalho, várias ferramentas

Você reuniu alguns dados - em um ArrayList, um HashSet, um HashMap - e agora quer visitar cada elemento. Java oferece algumas maneiras de fazer isso, e qual você escolhe depende de se você precisa do índice, de se precisa remover itens no meio do laço e de se prefere uma sintaxe de método ou de laço.

A boa notícia: cada Collection (lista, conjunto, fila) suporta o mesmo laço for aprimorado, então, depois de aprendê-lo, você pode iterar todas elas da mesma forma.

Leia os dois-pontos como "em": "para cada lang em langs". Você nunca toca em um índice, então não há nada para errar.

O laço for-each

O for aprimorado é a escolha padrão para o simples "faça algo com cada elemento". Ele se lê com clareza e funciona de forma idêntica entre os tipos de coleção - aqui um HashSet, que não tem índices nenhum:

Uma coisa para lembrar sobre um HashSet: ele não tem ordem definida, então os elementos podem ser impressos em qualquer sequência. O for-each visita todos eles exatamente uma vez, de qualquer forma.

Quando você precisa do índice

O for-each fornece o elemento, mas não a sua posição. Se você realmente precisa do índice - para numerar linhas ou para olhar os elementos vizinhos - use um laço contado com size() e get(i). Isso funciona em uma List, que é posicional; conjuntos e mapas não têm índice, então esse estilo não se aplica a eles.

Não recorra a isso só por hábito. Se você não está usando i para nada além de get(i), a versão com for-each é mais curta e mais difícil de errar.

Iterando um Map

Um Map não é uma Collection, então você não pode fazer for-each diretamente sobre ele. Em vez disso, você percorre uma de suas três visões. A mais comum é entrySet(), que entrega cada par chave-valor junto:

Se você só precisa das chaves, percorra ages.keySet(); se você só precisa dos valores, percorra ages.values(). Prefira entrySet() quando precisar de ambos - percorrer as chaves e depois chamar ages.get(key) dentro faz uma segunda busca a cada iteração sem motivo nenhum.

O Iterator

O for-each é, na verdade, açúcar sintático sobre um Iterator - um objeto que percorre uma coleção um elemento por vez via hasNext() e next(). Você raramente escreve esse laço à mão, com uma exceção importante: é a forma segura de remover elementos durante a iteração.

it.remove() apaga o elemento que next() retornou por último, e o iterador permanece válido. Esta é a única forma autorizada de modificar uma coleção durante um laço manual.

A armadilha: ConcurrentModificationException

Se você chamar add ou remove na própria coleção dentro de um laço for-each, recebe uma ConcurrentModificationException - o iterador percebe que a coleção mudou por baixo dele e se recusa a continuar. Este é um dos erros em tempo de execução de iniciante mais comuns.

List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4));
for (int n : nums) {
    if (n % 2 == 0) {
        nums.remove(Integer.valueOf(n));   // throws ConcurrentModificationException
    }
}

A correção é quase sempre removeIf, que expressa a intenção em uma única linha e cuida da iteração para você:

removeIf funciona em qualquer Collection, então a mesma chamada também limpa um HashSet.

O método forEach

Toda coleção também tem um método forEach que recebe uma lambda e a executa sobre cada elemento. É uma alternativa mais funcional, em estilo de expressão, ao laço - prática para frases curtas de uma linha:

Note que Map.forEach recebe diretamente uma lambda de dois argumentos (key, value) - sem precisar de entrySet(). Use forEach para efeitos colaterais rápidos; volte ao laço for comum quando o corpo crescer ou você precisar de break para sair mais cedo, algo que uma lambda não pode fazer.

Próximo: Métodos

Você já empacotou dados em coleções e os percorreu de todas as formas que Java oferece. O próximo passo é empacotar comportamento: escrever seus próprios métodos para poder dar nome a um bloco de lógica, passar entradas a ele e reutilizá-lo - que é a próxima página.

Perguntas frequentes

Como percorrer uma lista em Java?

A forma mais limpa é o laço for aprimorado (for-each): for (String s : list) { ... }. Ele funciona com qualquer Collection - ArrayList, HashSet e assim por diante. Use um laço com índice e get(i) apenas quando realmente precisar da posição, e um Iterator quando precisar remover elementos durante o laço.

Como iterar sobre um HashMap em Java?

Um Map não é uma Collection, então você percorre uma de suas visões. A escolha usual é for (Map.Entry<K, V> e : map.entrySet()), que fornece tanto a chave (e.getKey()) quanto o valor (e.getValue()) em uma única passagem. Você também pode percorrer map.keySet() para as chaves ou map.values() para os valores.

Por que recebo uma ConcurrentModificationException ao iterar?

Você chamou add ou remove na coleção enquanto um laço for-each a iterava. O laço for-each usa um Iterator por baixo dos panos e ele detecta que a coleção mudou estruturalmente. Resolva removendo pelo próprio método remove() do Iterator, ou chamando removeIf(...) em vez de percorrer.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR