1. The Iterable interface
In Java, almost all collections (except Map) implement the Iterable interface. This means you can traverse them sequentially—element by element—without delving into internal details. For a developer it feels like “the collection has a built-in way to walk through all elements.”
The Iterable interface defines exactly one method:
Iterator<E> iterator();
The iterator() method returns an object of type Iterator—a “helper” that knows how to walk the collection step by step. Thanks to this, the familiar for-each loop works:
for (ElementType e : collection) {
// ...
}
— behind the scenes it is that very Iterator. An extra benefit: with it you can safely remove elements during traversal using remove(). If you try to do this with a regular enhanced for loop, you can easily hit a ConcurrentModificationException.
2. The Iterator interface
An Iterator is a “courier” that can walk your collection, not skipping elements and preserving its traversal order.
| Method | Description |
|---|---|
|
Are there more elements to iterate? |
|
Return the next element and advance to it |
|
Remove the current element safely, without failures |
Example: iterating a collection with an Iterator
import java.util.*;
public class IteratorDemo {
public static void main(String[] args) {
List<String> tasks = new ArrayList<>();
tasks.add("Pet the cat");
tasks.add("Do homework");
tasks.add("Watch a TV series");
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
System.out.println("Task: " + task);
}
}
}
What’s happening here?
- We get the iterator via tasks.iterator().
- While there is a next element (hasNext() returns true), we take it via next() and print it.
- The iterator itself tracks the traversal order—you don’t need to know how the collection stores elements internally.
3. Why do we need an Iterator if we have loops?
With an Iterator you can traverse any collection, even those without indices (for example, Set). It’s a universal approach that doesn’t depend on the concrete collection type.
Safe element removal
A common task: walk through a collection and remove some elements. If you do it with a for-each, you can get an error:
for (String task : tasks) {
if (task.contains("cat")) {
tasks.remove(task); // BOOM! ConcurrentModificationException
}
}
Why does this happen? The collection doesn’t expect its structure to be changed directly during an iteration driven by an iterator.
The correct way:
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
if (task.contains("cat")) {
it.remove(); // Everything will go smoothly!
}
}
Why can’t we just use indices?
Because not all collections have indices. For example, HashSet or TreeSet have no notion of “the fifth element.” Iterator works everywhere—that’s its strength.
4. Details about for-each
The enhanced for loop (for-each) appeared back in Java 5. Essentially it’s syntactic sugar that lets you iterate elements as simply as possible:
for (String task : tasks) {
System.out.println("Task: " + task);
}
Under the hood the compiler calls iterator(), checks elements via hasNext(), and retrieves them with next(). It reads almost like natural language: “for each task in the list.”
When is for-each not suitable?
- You need to remove elements during traversal (for-each doesn’t let you call remove() directly).
- You need access to the index to, for example, replace an element by position.
- You’re working with a Map—it has key–value pairs and needs its own iteration logic.
5. Iterating a Map: tips and nuances
The Map interface doesn’t implement Iterable directly because it’s a set of key–value pairs. Nevertheless, Map provides convenient views for iteration.
Iterating keys
Map<String, String> users = new HashMap<>();
users.put("john", "john@example.com");
users.put("peter", "peter@gmail.com");
for (String login : users.keySet()) {
System.out.println("Login: " + login);
}
Iterating values
for (String email : users.values()) {
System.out.println("Email: " + email);
}
Iterating pairs (key–value)
The most general approach is to iterate over entrySet():
for (Map.Entry<String, String> entry : users.entrySet()) {
System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}
Interesting fact: Entry is an inner interface of Map with methods getKey() and getValue(). This way you get both parts of the pair at once.
Iterating via an Iterator
Iterator<Map.Entry<String, String>> it = users.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
// You can even safely remove an element:
if (entry.getKey().startsWith("v")) {
it.remove();
}
}
6. Real-world examples: how iterating collections helps in an application
Example: print all user tasks
List<String> tasks = new ArrayList<>();
tasks.add("Do homework");
tasks.add("Pet the cat");
tasks.add("Watch a TV series");
System.out.println("Your tasks for today:");
for (String task : tasks) {
System.out.println("- " + task);
}
Now let’s delete all tasks containing the word "cat":
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
if (task.contains("cat")) {
it.remove();
}
}
System.out.println("Remaining tasks:");
for (String task : tasks) {
System.out.println("- " + task);
}
Example: iterate unique logins via Set
Set<String> logins = new HashSet<>();
logins.add("john");
logins.add("peter");
logins.add("mary");
for (String login : logins) {
System.out.println("User: " + login);
}
Note: the output order for a Set can be arbitrary!
Example: iterate a Map to display users
Map<String, String> users = new HashMap<>();
users.put("john", "john@example.com");
users.put("peter", "peter@gmail.com");
for (Map.Entry<String, String> entry : users.entrySet()) {
System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}
7. Iterator.remove(): safe element deletion
One of the most common beginner mistakes is attempting to delete collection elements during a for-each traversal. The iterator solves this with remove().
How does it work?
- When you call it.remove(), it deletes the current element—the one returned by the last next().
- It’s safe: the collection won’t throw a ConcurrentModificationException.
Example:
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(); // Remove all even numbers
}
}
System.out.println(numbers); // [1, 3, 5]
Collection traversal diagram
+---------+ +---------+ +---------+
| Element | --> | Element | --> | Element | ...
+---------+ +---------+ +---------+
^ ^
| |
next() next()
The iterator “steps” through the elements until hasNext() returns false.
9. Common mistakes when working with Iterator and iterating collections
Error #1: modifying a collection while iterating with for-each.
Attempting to remove an element directly inside for-each leads to ConcurrentModificationException:
for (String task : tasks) {
if (task.contains("cat")) {
tasks.remove(task); // BOOM! ConcurrentModificationException
}
}
Use an Iterator and its remove().
Error #2: calling remove() before next().
You must first obtain the current element via next(); otherwise the iterator doesn’t know what to remove.
Iterator<String> it = tasks.iterator();
it.remove(); // Error! next() is required first
Error #3: attempting to iterate a Map directly in for-each.
Map doesn’t implement Iterable directly—use keySet(), values(), or entrySet().
Map<String, String> users = new HashMap<>();
// for (String entry : users) { ... } // Error: you can't do that
for (Map.Entry<String, String> e : users.entrySet()) {
// correct
}
Error #4: modifying the collection outside the iterator during an iterator-driven traversal.
While iterating, remove elements only via it.remove(), not via the collection’s methods.
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
if (task.contains("cat")) {
tasks.remove(task); // Error! You should use it.remove()
}
}
GO TO FULL VERSION