Java Iterator and Iterable

In the last lesson you learned about the Java Collection interface. Every collection must also hand you its items one at a time so you can walk through them. The thing that does that walking is the Iterator, and the reason a for-each loop works at all is a partner interface called Iterable.

🤔 The problem: how does a for-each loop even work?

You have written this loop many times.

import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Riya");
names.add("Arjun");
for (String name : names) {
System.out.println(name);
}
}
}

This prints each name on its own line. But how does the loop know there is a “next” name? How does it know when to stop? A List, a Set, a Map of keys all loop with the same for-each, yet they store data differently inside. So something common sits underneath them all.

Output

Alex
Riya
Arjun

That common thing is the Iterator. Every collection knows how to make one, and the for-each loop quietly uses it. Once you see this, you understand why some loops crash when you delete an item, and the safe way to fix it.

🧩 What is Iterable?

Iterable is an interface with one job. A class that implements it promises “I can give you an iterator.” It has one main method.

Iterator<T> iterator();

That is the whole contract:

  • If a class is Iterable, you can use it in a for-each loop.
  • List, Set, Queue, and every collection implement it through the Collection interface.
  • A type that is not Iterable will not compile in a for-each loop.

So Iterable is the door, and iterator() is the key it hands you.

🔁 What is an Iterator?

An Iterator is the little object that walks through the items, like a finger pointing at a spot. It has three methods.

  • hasNext() returns true if there is still another item to visit.
  • next() returns the next item and moves the finger forward.
  • remove() deletes the item that next() just returned.

The example below grabs the iterator by hand and loops with it. This is the long form of what a for-each does for you.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Riya");
names.add("Arjun");
Iterator<String> it = names.iterator(); // get the iterator
while (it.hasNext()) { // any more items?
String name = it.next(); // grab one, move forward
System.out.println(name);
}
}
}

We ask the list for its iterator. While there is a next item, we pull it out with next() and print it. When hasNext() returns false, the loop stops. You will rarely write this by hand, but knowing it makes the rest of this lesson click.

Output

Alex
Riya
Arjun

🍬 The for-each loop is just sugar over an Iterator

Here is the big reveal. A for-each loop is not magic. When you write this:

for (String name : names) {
System.out.println(name);
}

the Java compiler quietly turns it into this:

Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}

They compile to the same thing. The for-each is syntactic sugar, a shorter way to write code that the compiler expands into the longer form. Because the loop secretly uses an iterator, changing the collection behind its back causes the most common loop bug in Java, which we hit next.

💥 The crash: ConcurrentModificationException

Say you want to remove every name starting with “A” while looping. The natural first try is a for-each with a remove inside. It looks fine. It is not.

import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Riya");
names.add("Arjun");
for (String name : names) {
if (name.startsWith("A")) {
names.remove(name); // ❌ changing the list mid-loop
}
}
System.out.println(names);
}
}

This throws an error and stops your program. The list noticed it was changed while an iterator was walking it, and it refused to go on.

Output

Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1095)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1049)
at Main.main(Main.java:12)

Why does this happen?

  • The for-each uses a hidden iterator, which keeps a count of how many times the list changed.
  • Calling names.remove(...) directly bumps the list’s change-count, but the iterator does not know.
  • On the next next(), the iterator sees the mismatch and throws ConcurrentModificationException.

The name means “the collection was modified while we were going through it.”

✅ The fix: iterator.remove()

The Iterator has its own remove() method. When it removes the item, it updates its own bookkeeping. No mismatch, no exception.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Riya");
names.add("Arjun");
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
if (name.startsWith("A")) {
it.remove(); // ✅ safe removal through the iterator
}
}
System.out.println(names);
}
}

We loop with the iterator by hand. To drop an item, we call it.remove() instead of names.remove(...). It deletes the item the last next() returned, and stays in sync. No exception. The “A” names are gone, “Riya” remains.

Output

[Riya]

Call next() before remove()

it.remove() deletes whatever the last it.next() returned. So you must call next() first, then remove(). Calling remove() twice in a row, or before any next(), throws IllegalStateException. One next() gives you one allowed remove().

On Java 8 or newer there is a shorter way. The removeIf method takes a condition and does the safe iterator removal for you.

import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Riya");
names.add("Arjun");
names.removeIf(name -> name.startsWith("A")); // ✅ clean and safe
System.out.println(names);
}
}

removeIf drops every item that matches, using a safe iterator underneath. One line, no exception, same result. This is the cleanest choice when you delete by a rule.

Output

[Riya]

↔️ ListIterator: walking both ways

A plain Iterator only moves forward. A List gives you a stronger ListIterator:

  • It goes forward and backward.
  • It can tell you the index.
  • It can replace or add items as it walks.

You get it with listIterator().

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alex");
names.add("Riya");
names.add("Arjun");
ListIterator<String> it = names.listIterator();
while (it.hasNext()) {
String name = it.next();
it.set(name.toUpperCase()); // replace as we go
}
// now walk backward
while (it.hasPrevious()) {
System.out.println(it.previous());
}
}
}

We walk forward once, uppercasing each name with set. The iterator now sits at the end, so we walk back with hasPrevious() and previous(), printing in reverse. ListIterator adds hasPrevious(), previous(), set(), and add() on top of the normal iterator. Reach for it when you need to go backward or edit in place.

Output

ARJUN
RIYA
ALEX

🛠️ Making your own class Iterable

Because Iterable is just an interface, you can make your own class work in a for-each loop. You implement Iterable and return an Iterator. Here is a Team class that holds names and lets you loop straight over it.

import java.util.Iterator;
public class Main {
static class Team implements Iterable<String> {
private String[] members = {"Alex", "Riya", "Arjun"};
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < members.length;
}
@Override
public String next() {
return members[index++];
}
};
}
}
public static void main(String[] args) {
Team team = new Team();
for (String member : team) { // ✅ works because Team is Iterable
System.out.println(member);
}
}
}

Team implements Iterable, so it provides an iterator(). Inside, we return an iterator that tracks an index. Its hasNext() checks if the index is still inside the array, and next() returns the current item and steps forward. Now the for-each loop accepts Team like a built-in collection. That is the secret behind every loopable type in Java.

Output

Alex
Riya
Arjun

⚠️ Common Mistakes

A few iterator traps that catch almost everyone.

Modifying a collection inside a for-each loop. The classic ConcurrentModificationException. The hidden iterator breaks when you change the collection directly.

// ❌ Wrong: removing from the collection during a for-each
for (String name : names) {
if (name.startsWith("A")) {
names.remove(name); // throws ConcurrentModificationException
}
}
// ✅ Right: use the iterator's own remove(), or removeIf()
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (it.next().startsWith("A")) {
it.remove();
}
}

Calling next() without checking hasNext(). If there is no next item, next() throws NoSuchElementException. Always guard it.

// ❌ Wrong: calling next() blindly, may run past the end
Iterator<String> it = names.iterator();
String first = it.next();
String second = it.next(); // crashes if the list has only one item
// ✅ Right: check hasNext() before every next()
while (it.hasNext()) {
System.out.println(it.next());
}

Calling remove() before next() or twice in a row. The iterator’s remove() only works once per next(). Doing it out of order throws IllegalStateException.

✅ Best Practices

Habits for looping the right way.

  • Use a normal for-each loop when you are only reading items. It is the cleanest form.
  • When you must delete items while looping, use the iterator’s remove() or, even better, removeIf().
  • Always call hasNext() before next(), so you never run off the end.
  • Remember each next() allows exactly one remove(). Do not call remove() twice or before the first next().
  • Reach for ListIterator only when you actually need to go backward or edit items in place.
  • Make your own class Iterable when it holds a group of things, so callers can loop over it naturally.

🧩 What You’ve Learned

Nicely done. Let’s recap iterators and iterables.

  • Iterable is the interface that lets an object work in a for-each loop; it provides iterator().
  • ✅ An Iterator walks through items with hasNext(), next(), and remove().
  • ✅ A for-each loop is syntactic sugar the compiler expands into an iterator-based while loop.
  • ✅ Changing a collection during a for-each throws ConcurrentModificationException.
  • ✅ The safe fix is the iterator’s own remove(), or removeIf() on Java 8 and up.
  • ListIterator adds backward movement and in-place editing for lists.
  • ✅ You can make your own class Iterable by implementing iterator().

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does the Iterable interface let you do?

    Why: A class that implements Iterable provides an iterator(), which is what makes a for-each loop work on it.

  2. 2

    Which methods does an Iterator have?

    Why: An Iterator exposes hasNext() to check for more items, next() to get one, and remove() to delete the last returned item.

  3. 3

    Why does removing from a collection inside a for-each loop throw ConcurrentModificationException?

    Why: A for-each is sugar over an iterator; changing the collection directly makes the iterator's change-count mismatch, so it throws.

  4. 4

    What is the safe way to remove items while looping?

    Why: The iterator's remove() (or removeIf()) deletes items while keeping the iterator in sync, avoiding the exception.

🚀 What’s Next?

You now understand the machinery behind every loop in Java. Next we go deeper into the most used collection of all, the one that keeps items in order and lets you reach them by position. Let’s learn the List interface.

Java List Interface

Share & Connect