Java Iterator and Iterable
Table of Contents + −
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
AlexRiyaArjunThat 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 afor-eachloop. List,Set,Queue, and every collection implement it through theCollectioninterface.- A type that is not
Iterablewill not compile in afor-eachloop.
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()returnstrueif there is still another item to visit.next()returns the next item and moves the finger forward.remove()deletes the item thatnext()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
AlexRiyaArjun🍬 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-eachuses 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 throwsConcurrentModificationException.
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
ARJUNRIYAALEX🛠️ 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
AlexRiyaArjun⚠️ 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-eachfor (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 endIterator<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-eachloop 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()beforenext(), so you never run off the end. - Remember each
next()allows exactly oneremove(). Do not callremove()twice or before the firstnext(). - Reach for
ListIteratoronly when you actually need to go backward or edit items in place. - Make your own class
Iterablewhen 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(), andremove(). - ✅ A for-each loop is syntactic sugar the compiler expands into an iterator-based
whileloop. - ✅ Changing a collection during a
for-eachthrowsConcurrentModificationException. - ✅ The safe fix is the iterator’s own
remove(), orremoveIf()on Java 8 and up. - ✅
ListIteratoradds 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
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
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
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
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.