CodeGym /Cursos /JAVA 25 SELF /Iterable e Iterator: percorrendo coleções

Iterable e Iterator: percorrendo coleções

JAVA 25 SELF
Nível 27 , Lição 1
Disponível

1. Interface Iterable

Em Java, quase todas as coleções (exceto Map) implementam a interface Iterable. Isso significa que podemos percorrê-las sequencialmente — elemento por elemento — sem conhecer os detalhes da estrutura interna. Para o programador, isso se traduz em “a coleção tem uma forma embutida de percorrer todos os elementos”.

A interface Iterable define exatamente um método:

Iterator<E> iterator();

O método iterator() retorna um objeto do tipo Iterator — um “assistente” que sabe como percorrer a coleção passo a passo. Graças a ele, o laço for-each funciona:

for (ElementType e : collection) {
    // ...
}

— nos bastidores está justamente esse Iterator. Bônus adicional: com ele é possível remover elementos com segurança durante a iteração usando o método remove(). Se tentar fazer isso com um percurso “normal”, é fácil levar um ConcurrentModificationException.

2. Interface Iterator

Iterator é um “guia” que sabe percorrer sua coleção sem pular elementos e respeitando sua ordem de iteração.

Método Descrição
boolean hasNext()
Ainda há elementos a percorrer?
E next()
Retorna o próximo elemento e avança para ele
void remove()
Remove o elemento atual com segurança, sem falhas

Exemplo de iteração de uma coleção com Iterator

import java.util.*;

public class IteratorDemo {
    public static void main(String[] args) {
        List<String> tasks = new ArrayList<>();
        tasks.add("Fazer carinho no gato");
        tasks.add("Fazer a lição de casa");
        tasks.add("Assistir a uma série");

        Iterator<String> it = tasks.iterator();
        while (it.hasNext()) {
            String task = it.next();
            System.out.println("Tarefa: " + task);
        }
    }
}

O que está acontecendo aqui?

  • Obtemos o iterador via tasks.iterator().
  • Enquanto houver próximo elemento (hasNext() retorna true), pegamos com next() e imprimimos.
  • O iterador cuida da ordem de percurso — você não precisa saber como a coleção armazena os elementos internamente.

3. Por que precisamos de Iterator se já temos laços?

Com o Iterator é possível percorrer qualquer coleção, mesmo sem índices (por exemplo, Set). É um método universal, que não depende do tipo específico de coleção.

Remoção segura de elementos

Tarefa comum: percorrer a coleção e remover alguns elementos. Se fizer isso com for-each, pode ocorrer um erro:

for (String task : tasks) {
    if (task.contains("gato")) {
        tasks.remove(task); // BOOM! ConcurrentModificationException
    }
}

Por que isso acontece? A coleção não espera que sua estrutura seja alterada diretamente durante o percurso iniciado pelo iterador.

Maneira correta:

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("gato")) {
        it.remove(); // Tudo vai ocorrer sem problemas!
    }
}

Por que não usar simplesmente índices?

Porque nem todas as coleções têm índices. Por exemplo, HashSet ou TreeSet não têm o conceito de “quinto elemento”. O Iterator sempre funciona — essa é a sua força.

4. Detalhes sobre for-each

O laço for aprimorado (for-each) surgiu no Java 5. Essencialmente é açúcar sintático que permite iterar elementos da forma mais simples:

for (String task : tasks) {
    System.out.println("Tarefa: " + task);
}

Por baixo dos panos, o compilador chama iterator(), verifica os elementos com hasNext() e obtém com next(). Lê-se literalmente como uma frase em linguagem natural: “para cada tarefa da lista”.

Quando o for-each não serve?

  • Você precisa remover elementos durante o percurso (o for-each não permite chamar remove() diretamente).
  • Você precisa de acesso ao índice para, por exemplo, substituir um elemento pela posição.
  • Você está trabalhando com Map — ela possui pares “chave-valor”, exigindo sua própria lógica de iteração.

5. Iterando Map: truques e nuances

A interface Map não implementa Iterable diretamente, pois é um conjunto de pares “chave-valor”. Mesmo assim, Map fornece visões convenientes para iteração.

Iteração pelas chaves

Map<String, String> users = new HashMap<>();
users.put("vasya", "vasya@example.com");
users.put("petya", "petya@gmail.com");

for (String login : users.keySet()) {
    System.out.println("Login: " + login);
}

Iteração pelos valores

for (String email : users.values()) {
    System.out.println("Email: " + email);
}

Iteração por pares (chave-valor)

A forma mais universal é iterar por entrySet():

for (Map.Entry<String, String> entry : users.entrySet()) {
    System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}

Fato interessante: Entry é uma interface interna de Map com os métodos getKey() e getValue(). Assim você obtém as duas partes do par de uma vez.

Iteração com Iterator

Iterator<Map.Entry<String, String>> it = users.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<String, String> entry = it.next();
    // Você pode até remover um elemento com segurança:
    if (entry.getKey().startsWith("v")) {
        it.remove();
    }
}

6. Exemplos práticos: como a iteração de coleções ajuda no aplicativo

Exemplo: listando todas as tarefas do usuário

List<String> tasks = new ArrayList<>();
tasks.add("Fazer a lição de casa");
tasks.add("Fazer carinho no gato");
tasks.add("Assistir a uma série");

System.out.println("Suas tarefas para hoje:");
for (String task : tasks) {
    System.out.println("- " + task);
}

Agora vamos remover todas as tarefas que contêm a palavra "gato":

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("gato")) {
        it.remove();
    }
}
System.out.println("Tarefas restantes:");
for (String task : tasks) {
    System.out.println("- " + task);
}

Exemplo: iterando logins únicos via Set

Set<String> logins = new HashSet<>();
logins.add("vasya");
logins.add("petya");
logins.add("masha");

for (String login : logins) {
    System.out.println("Usuário: " + login);
}

Atenção: a ordem de saída de um Set pode ser qualquer uma!

Exemplo: iterando uma Map para exibir usuários

Map<String, String> users = new HashMap<>();
users.put("vasya", "vasya@example.com");
users.put("petya", "petya@gmail.com");

for (Map.Entry<String, String> entry : users.entrySet()) {
    System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}

7. Iterator.remove(): remoção segura de elementos

Um dos erros mais comuns de iniciantes é tentar remover elementos da coleção durante a iteração com for-each. O iterador resolve esse problema com remove().

Como isso funciona?

  • Ao chamar it.remove(), o elemento atual é removido — aquele retornado pelo último next().
  • É seguro: a coleção não lança ConcurrentModificationException.

Exemplo:

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    int n = it.next();
    if (n % 2 == 0) {
        it.remove(); // Removemos todos os números pares
    }
}
System.out.println(numbers); // [1, 3, 5]

Esquema de iteração de uma coleção

+---------+     +---------+     +---------+
| Element | --> | Element | --> | Element | ...
+---------+     +---------+     +---------+
     ^               ^
     |               |
   next()         next()

O iterador “caminha” pelos elementos enquanto hasNext() não retornar false.

9. Erros típicos ao trabalhar com Iterator e percorrer coleções

Erro nº 1: modificar a coleção durante o percurso com for-each.
Tentar remover um elemento dentro do for-each leva a ConcurrentModificationException:

for (String task : tasks) {
    if (task.contains("gato")) {
        tasks.remove(task); // BOOM! ConcurrentModificationException
    }
}

Use o Iterator e seu remove().

Erro nº 2: chamar remove() antes de next().
Primeiro você precisa obter o elemento atual com next(); caso contrário, o iterador não sabe o que remover.

Iterator<String> it = tasks.iterator();
it.remove(); // Erro! Primeiro é preciso next()

Erro nº 3: tentar iterar Map diretamente em um for-each.
Map não implementa Iterable diretamente — use keySet(), values() ou entrySet().

Map<String, String> users = new HashMap<>();
// for (String entry : users) { ... } // Erro: não pode ser assim
for (Map.Entry<String, String> e : users.entrySet()) {
    // correto
}

Erro nº 4: alterar a coleção fora do iterador enquanto itera com ele.
Durante o percurso, remova elementos apenas por it.remove(), não pelos métodos da coleção.

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("gato")) {
        tasks.remove(task); // Erro! É preciso it.remove()
    }
}
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION