Hello! In our previous lessons, we've filled our toolbox with the fundamental data structures from the Java Collections Framework: ArrayList, LinkedList, HashSet, TreeSet, HashMap, and TreeMap. You've learned how to store and organize data using these powerful classes. Now, the next logical step is to learn how to access and process that data.
This lesson focuses on the two primary ways to traverse any collection in Java: the Iterator interface and the enhanced for-each loop. Mastering these mechanisms is crucial for a backend developer, as you will constantly need to loop through lists of database records, process sets of unique identifiers, or handle entries in configuration maps. We'll explore how these tools work, why they are designed the way they are, and when to choose one over the other.
1. A Universal Approach to Traversal: Iterable and Iterator
Imagine you have data in an ArrayList, a HashSet, and a TreeMap. Each has a different internal structure. How can you write code that accesses elements from any of them without needing to know their specific implementation details? This is the problem that Java's Iterator design pattern solves. It provides a clean, universal contract for sequential access.
The foundation for this is the java.lang.Iterable interface. Any class that implements Iterable is signaling, "You can loop over me." All the collection classes we've studied (ArrayList, HashSet, etc.) implement this interface.
The Iterable interface has one crucial abstract method: iterator(). This method returns an Iterator object. The Iterator is the actual "cursor" that you use to walk through the collection.
The video below from Programming with Mosh provides an excellent explanation of why this separation is so important and how the Iterable and Iterator interfaces work together to decouple your code from a collection's internal structure.
This video masterfully explains the problem of iteration and how the Iterable interface solves it. It's a key concept for understanding good object-oriented design.
Please watch the following segments: The Problem: The instructor sets up a scenario showing why exposing a collection's internal array for iteration is a bad practice that breaks encapsulation. The Iterable Solution: This part introduces the Iterable and Iterator interfaces as the solution, explaining that Iterable promises the class can provide an Iterator. How Iterator Works: Here, you'll see how to use an Iterator with a while loop. Pay close attention to the brilliant reveal where the instructor inspects the bytecode to show that the familiar for-each loop is actually just "syntactic sugar" for this exact Iterator logic.
2. Using the Iterator
As you saw in the video, the Iterator interface has three core methods you need to know:
boolean hasNext(): Returnstrueif there are more elements to iterate over.E next(): Returns the next element in the iteration and advances the cursor.void remove(): Removes the last element returned bynext()from the underlying collection.
This diagram illustrates the relationship. An Iterable object provides an Iterator.

The typical way to use an iterator is with a while loop. You get the iterator from the collection, then loop as long as hasNext() is true, processing each element you get from next().

Let's look at a practical demonstration.
This video from Alex Lee provides a straightforward, hands-on example of using an Iterator.
Watch these two segments to see it in action: Creating an Iterator: See how to get an Iterator from an ArrayList and call next() to get elements. The while loop: This shows the standard pattern of using while (it.hasNext()) to loop through the entire collection safely.
The code for iterating through a list of strings would look like this:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorDemo {
public static void main(String[] args) {
List<String> languages = new ArrayList<>();
languages.add("Java");
languages.add("JavaScript");
languages.add("Python");
// 1. Get the iterator from the collection
Iterator<String> iterator = languages.iterator();
// 2. Loop using hasNext() and next()
System.out.println("Iterating with a while loop:");
while (iterator.hasNext()) {
String language = iterator.next();
System.out.println(language);
}
}
}
3. The Enhanced For-Each Loop
As Mosh's video revealed, the while loop with an Iterator is exactly what the Java compiler generates when you use an enhanced for-each loop. The for-each loop is a cleaner, more readable syntax for the common case of simply wanting to access each element in a collection.
Using a for-each loop, the example above becomes much more concise:
// ... same setup as before
System.out.println("\nIterating with a for-each loop:");
for (String language : languages) {
System.out.println(language);
}
This code is easier to write and read, and it achieves the exact same result. Because it works with any object that implements Iterable, you can use this exact syntax for an ArrayList, a HashSet, or any other collection class.
4. Iterator vs. For-Each: The Critical Difference
If the for-each loop is just simpler syntax for an Iterator, why would you ever use an Iterator directly? The answer lies in one critical capability: modification.
You must use an Iterator to safely remove elements from a collection while you are iterating over it.
If you try to remove an element from a collection inside a for-each loop (or a while loop using the collection's own remove() method), you will get a ConcurrentModificationException. This happens because the iterator's internal state becomes out of sync with the collection's state. The iterator's remove() method is specifically designed to handle this by updating both the collection and its own state correctly.
The following reading and video demonstrate this crucial distinction.
This GeeksforGeeks article gets straight to the point, highlighting the key difference between the two iteration methods.
Please read the following sections: Start at the difference, which clearly states that you can't modify a collection in a for-each loop. Continue to the section When to use which traversal?, which provides the primary rule: use an Iterator if you need to modify the collection.
Now, let's see a practical example of safely removing items with iterator.remove().
This final segment from Alex Lee's video shows a perfect use case for the iterator's remove() method.
Watch the example from where he removes elements from a list of numbers that are less than 50. Notice that the remove() call happens on the iterator object, not the list itself.
Here is a code example demonstrating the right way and the wrong way to remove elements.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class RemovalDemo {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
fruits.add("Avocado");
// SAFE REMOVAL using Iterator.remove()
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if (fruit.startsWith("A")) {
iterator.remove(); // Correct!
}
}
System.out.println("Fruits after safe removal: " + fruits); // [Banana, Cherry]
// UNSAFE REMOVAL using for-each (will throw Exception)
List<String> vegetables = new ArrayList<>();
vegetables.add("Broccoli");
vegetables.add("Beetroot");
vegetables.add("Carrot");
try {
for (String veg : vegetables) {
if (veg.startsWith("B")) {
vegetables.remove(veg); // Unsafe! Throws ConcurrentModificationException
}
}
} catch (Exception e) {
System.out.println("\nException caught: " + e);
}
}
}
Decision Summary:
- Use the for-each loop for all read-only iteration. It is cleaner, simpler, and less error-prone. This will be your choice >95% of the time.
- Use the
Iteratorwith awhileloop specifically when you need to calliterator.remove()to delete elements from the collection during the loop.
5. Iterating Over Maps
What about Map? The Map interface itself does not extend Iterable. However, it provides three methods that return Collection views, which are iterable:
keySet(): Returns aSetof all the keys in the map.values(): Returns aCollectionof all the values in the map.entrySet(): Returns aSetofMap.Entry<K, V>objects. EachEntryobject contains both the key and the value for one pair.
Iterating over the entrySet is usually the most efficient approach if you need both the key and the value during the loop.
import java.util.HashMap;
import java.util.Map;
public class MapIterationDemo {
public static void main(String[] args) {
Map<String, String> capitals = new HashMap<>();
capitals.put("India", "New Delhi");
capitals.put("USA", "Washington D.C.");
capitals.put("Japan", "Tokyo");
// Most efficient way to iterate if you need both key and value
System.out.println("Iterating over entrySet():");
for (Map.Entry<String, String> entry : capitals.entrySet()) {
String country = entry.getKey();
String capital = entry.getValue();
System.out.println("The capital of " + country + " is " + capital);
}
// Iterating over keys only
System.out.println("\nIterating over keySet():");
for (String country : capitals.keySet()) {
System.out.println("Country: " + country);
}
// Iterating over values only
System.out.println("\nIterating over values():");
for (String capital : capitals.values()) {
System.out.println("Capital: " + capital);
}
}
}
Conclusion
You now have a complete understanding of how to traverse collections in Java. You've seen that the Iterator provides a powerful, universal mechanism for accessing elements, while the for-each loop offers a convenient and readable syntax for the most common iteration tasks.
Key Takeaways:
- The
Iterableinterface signals that a class can be looped over, and all standard Java collections implement it. Iterableprovides anIterator, which is an object used to traverse a collection withhasNext()andnext().- The enhanced for-each loop is syntactic sugar for using an
Iteratorand should be your default choice for read-only iteration. - To modify a collection while iterating, you must use the
Iterator'sremove()method to avoid aConcurrentModificationException. - To iterate over a
Map, you can use a for-each loop on itskeySet(),values(), orentrySet().
In our next lesson, we will address a new question: now that we can iterate over collections of our own custom objects, how do we tell Java how to sort them? We will explore the Comparable and Comparator interfaces to define natural and custom ordering for our classes.