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 |
|---|---|
|
Ainda há elementos a percorrer? |
|
Retorna o próximo elemento e avança para ele |
|
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()
}
}
GO TO FULL VERSION