Iterators in Java
Learn how to traverse collections element by element
💡 Think of an Iterator like reading a book page by page - you start at the beginning, check if there are more pages (hasNext), turn to the next page (next), and sometimes you might tear out a page (remove). You can only move forward, one page at a time!
📖 What is an Iterator?
An Iterator is an object that allows you to traverse through a collection, one element at a time. It provides a uniform way to access elements of different collection types without knowing their internal structure. Think of it as a bookmark that moves through your collection!
import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class BasicIterator { public static void main(String[] args) { // Create a list List<String> fruits = new ArrayList<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); System.out.println("Original list: " + fruits); // Get an iterator Iterator<String> iterator = fruits.iterator(); // Basic iteration pattern System.out.println("\nIterating through list:"); while (iterator.hasNext()) { // Check if more elements exist String fruit = iterator.next(); // Get next element System.out.println("- " + fruit); } // Iterator is now exhausted - need to create new one System.out.println("\nIterator exhausted. Creating new one..."); iterator = fruits.iterator(); // Iterate and count int count = 0; while (iterator.hasNext()) { iterator.next(); count++; } System.out.println("Total elements: " + count); // What happens if you call next() without hasNext()? iterator = fruits.iterator(); try { // Skip hasNext() checks - dangerous! iterator.next(); // Apple iterator.next(); // Banana iterator.next(); // Orange iterator.next(); // Mango iterator.next(); // NoSuchElementException! } catch (Exception e) { System.out.println("\nError: " + e.getClass().getSimpleName()); System.out.println("Always use hasNext() before next()!"); } }}🔍 🔄 Types of Iterators
Java provides different iterator types for different traversal needs:
Iterator
Basic forward-only iteration with hasNext(), next(), and remove() methods
ListIterator
Bidirectional iteration for Lists, can move forward and backward, add/set elements
Enhanced For-Loop
Simplified syntax using Iterator internally, read-only, cleaner code
forEach() Method
Functional approach using lambda expressions, very concise
import java.util.*;public class IterationMethods { public static void main(String[] args) { List<String> languages = Arrays.asList("Java", "Python", "JavaScript", "C++", "Go"); // Method 1: Iterator (explicit) System.out.println("1. Using Iterator:"); Iterator<String> iterator = languages.iterator(); while (iterator.hasNext()) { System.out.println(" - " + iterator.next()); } // Method 2: Enhanced for-loop (implicit Iterator) System.out.println("\n2. Using Enhanced For-Loop:"); for (String lang : languages) { System.out.println(" - " + lang); } // Method 3: Traditional for-loop with index (for Lists only) System.out.println("\n3. Using Index-Based Loop:"); for (int i = 0; i < languages.size(); i++) { System.out.println(" " + i + ": " + languages.get(i)); } // Method 4: forEach with lambda (Java 8+) System.out.println("\n4. Using forEach() Method:"); languages.forEach(lang -> System.out.println(" - " + lang)); // Method 5: forEach with method reference (Java 8+) System.out.println("\n5. Using Method Reference:"); languages.forEach(System.out::println); // Method 6: Stream API (Java 8+) System.out.println("\n6. Using Streams:"); languages.stream() .filter(lang -> lang.length() > 4) .forEach(lang -> System.out.println(" - " + lang)); }}🗑️ Removing Elements During Iteration
import java.util.*;public class RemovingWhileIterating { public static void main(String[] args) { // ===== WRONG WAY - ConcurrentModificationException ===== List<Integer> numbers1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); System.out.println("WRONG: Removing directly from collection"); try { for (Integer num : numbers1) { if (num % 2 == 0) { numbers1.remove(num); // DANGER! Modifying during iteration } } } catch (ConcurrentModificationException e) { System.out.println("Error: " + e.getClass().getSimpleName()); System.out.println("Cannot modify collection directly during iteration!\n"); } // ===== CORRECT WAY 1 - Using Iterator.remove() ===== List<Integer> numbers2 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); System.out.println("CORRECT: Using Iterator.remove()"); Iterator<Integer> iterator = numbers2.iterator(); while (iterator.hasNext()) { Integer num = iterator.next(); if (num % 2 == 0) { iterator.remove(); // Safe removal! } } System.out.println("Result: " + numbers2); // [1, 3, 5, 7, 9] // ===== CORRECT WAY 2 - Using removeIf() ===== List<Integer> numbers3 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); System.out.println("\nCORRECT: Using removeIf()"); numbers3.removeIf(num -> num % 2 == 0); // Remove all even numbers System.out.println("Result: " + numbers3); // [1, 3, 5, 7, 9] // ===== CORRECT WAY 3 - Collect to new list ===== List<Integer> numbers4 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); System.out.println("\nCORRECT: Filtering to new list"); List<Integer> oddNumbers = new ArrayList<>(); for (Integer num : numbers4) { if (num % 2 != 0) { oddNumbers.add(num); } } System.out.println("Result: " + oddNumbers); // [1, 3, 5, 7, 9] // ===== Real-world example: Removing inactive users ===== List<String> users = new ArrayList<>( Arrays.asList("active_alice", "inactive_bob", "active_charlie", "inactive_david") ); System.out.println("\nRemoving inactive users:"); System.out.println("Before: " + users); Iterator<String> userIterator = users.iterator(); while (userIterator.hasNext()) { String user = userIterator.next(); if (user.startsWith("inactive_")) { System.out.println(" Removing: " + user); userIterator.remove(); } } System.out.println("After: " + users); }}↔️ ListIterator: Bidirectional Traversal
import java.util.*;public class ListIteratorExample { public static void main(String[] args) { List<String> cities = new ArrayList<>( Arrays.asList("New York", "London", "Tokyo", "Paris", "Sydney") ); System.out.println("Original list: " + cities); // Get a ListIterator ListIterator<String> listIterator = cities.listIterator(); // ===== FORWARD ITERATION ===== System.out.println("\n=== Forward Iteration ==="); while (listIterator.hasNext()) { int index = listIterator.nextIndex(); String city = listIterator.next(); System.out.println(index + ": " + city); } // Now at the end of list // ===== BACKWARD ITERATION ===== System.out.println("\n=== Backward Iteration ==="); while (listIterator.hasPrevious()) { int index = listIterator.previousIndex(); String city = listIterator.previous(); System.out.println(index + ": " + city); } // ===== MODIFYING DURING ITERATION ===== System.out.println("\n=== Modifying Elements ==="); // Start fresh from beginning listIterator = cities.listIterator(); while (listIterator.hasNext()) { String city = listIterator.next(); // Replace "Tokyo" with "Osaka" if (city.equals("Tokyo")) { listIterator.set("Osaka"); System.out.println("Changed Tokyo to Osaka"); } // Add "Berlin" after "London" if (city.equals("London")) { listIterator.add("Berlin"); System.out.println("Added Berlin after London"); } } System.out.println("\nModified list: " + cities); // ===== STARTING FROM SPECIFIC INDEX ===== System.out.println("\n=== Starting from index 2 ==="); listIterator = cities.listIterator(2); // Start from index 2 while (listIterator.hasNext()) { System.out.println(" - " + listIterator.next()); } // ===== PRACTICAL EXAMPLE: Reverse a list ===== System.out.println("\n=== Reversing list ==="); List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); System.out.println("Original: " + numbers); List<Integer> reversed = new ArrayList<>(); ListIterator<Integer> numIterator = numbers.listIterator(numbers.size()); while (numIterator.hasPrevious()) { reversed.add(numIterator.previous()); } System.out.println("Reversed: " + reversed); }}⚠️ Understanding Fail-Fast Behavior
import java.util.*;public class FailFastBehavior { public static void main(String[] args) { List<String> items = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E")); // ===== FAIL-FAST: Throws ConcurrentModificationException ===== System.out.println("Demonstrating Fail-Fast Behavior:\n"); // Example 1: Modifying during enhanced for-loop System.out.println("1. Modifying during enhanced for-loop:"); try { for (String item : items) { System.out.println(" Processing: " + item); if (item.equals("C")) { items.remove(item); // FAIL-FAST! } } } catch (ConcurrentModificationException e) { System.out.println(" ❌ ConcurrentModificationException thrown!\n"); } // Example 2: Modifying with regular iterator items = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E")); System.out.println("2. Modifying collection (not iterator) during iteration:"); Iterator<String> iterator = items.iterator(); try { while (iterator.hasNext()) { String item = iterator.next(); System.out.println(" Processing: " + item); if (item.equals("C")) { items.remove(item); // FAIL-FAST! Modifying collection directly } } } catch (ConcurrentModificationException e) { System.out.println(" ❌ ConcurrentModificationException thrown!\n"); } // ===== SAFE WAYS ===== items = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E")); // Safe way 1: Use iterator.remove() System.out.println("3. SAFE: Using iterator.remove():"); iterator = items.iterator(); while (iterator.hasNext()) { String item = iterator.next(); System.out.println(" Processing: " + item); if (item.equals("C")) { iterator.remove(); // ✅ SAFE! System.out.println(" ✓ Removed C safely"); } } System.out.println(" Result: " + items + "\n"); // Safe way 2: Use removeIf() items = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E")); System.out.println("4. SAFE: Using removeIf():"); items.removeIf(item -> item.equals("C")); // ✅ SAFE! System.out.println(" Result: " + items + "\n"); // Safe way 3: Copy to avoid modification issues items = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E")); System.out.println("5. SAFE: Iterating over copy:"); List<String> itemsCopy = new ArrayList<>(items); for (String item : itemsCopy) { System.out.println(" Processing: " + item); if (item.equals("C")) { items.remove(item); // ✅ SAFE! Iterating copy, modifying original System.out.println(" ✓ Removed C safely"); } } System.out.println(" Result: " + items); // ===== WHY FAIL-FAST? ===== System.out.println("\n=== Why Fail-Fast? ==="); System.out.println("Fail-fast iterators detect concurrent modifications"); System.out.println("and throw exceptions to prevent unpredictable behavior."); System.out.println("This helps catch bugs early during development!"); }}⚡ Iterator vs Enhanced For-Loop
import java.util.*;public class IteratorVsForLoop { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(); for (int i = 1; i <= 10; i++) { numbers.add(i); } // ===== ITERATOR - Explicit Control ===== System.out.println("Using Iterator (explicit):"); Iterator<Integer> iterator = numbers.iterator(); while (iterator.hasNext()) { Integer num = iterator.next(); System.out.print(num + " "); // Can remove during iteration if (num % 3 == 0) { iterator.remove(); } } System.out.println("\nAfter removal: " + numbers); // Reset for next example numbers.clear(); for (int i = 1; i <= 10; i++) { numbers.add(i); } // ===== ENHANCED FOR-LOOP - Clean Syntax ===== System.out.println("\nUsing Enhanced For-Loop:"); for (Integer num : numbers) { System.out.print(num + " "); // Cannot remove here - would throw ConcurrentModificationException } // ===== WHEN TO USE WHICH ===== System.out.println("\n\n=== When to Use Which ==="); System.out.println("\nUse Enhanced For-Loop when:"); System.out.println(" ✓ Just reading elements"); System.out.println(" ✓ Want cleaner, more readable code"); System.out.println(" ✓ Don't need to modify collection"); System.out.println("\nUse Iterator when:"); System.out.println(" ✓ Need to remove elements"); System.out.println(" ✓ Need fine control over iteration"); System.out.println(" ✓ Iterating multiple collections in parallel"); // ===== PARALLEL ITERATION EXAMPLE ===== System.out.println("\n=== Parallel Iteration with Two Lists ==="); List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<Integer> ages = Arrays.asList(25, 30, 28); Iterator<String> nameIter = names.iterator(); Iterator<Integer> ageIter = ages.iterator(); while (nameIter.hasNext() && ageIter.hasNext()) { System.out.println(nameIter.next() + " is " + ageIter.next() + " years old"); } // ===== DIFFERENT COLLECTIONS ===== System.out.println("\n=== Works with All Collections ==="); // Set (no index access) Set<String> colors = new HashSet<>(Arrays.asList("Red", "Green", "Blue")); System.out.println("\nSet iteration:"); for (String color : colors) { System.out.println(" - " + color); } // Map (special iteration) Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 95); scores.put("Bob", 87); scores.put("Charlie", 92); System.out.println("\nMap iteration:"); for (Map.Entry<String, Integer> entry : scores.entrySet()) { System.out.println(" " + entry.getKey() + ": " + entry.getValue()); } }}🎯 Real-World Example: Filtering and Processing
import java.util.*;class Transaction { String id; double amount; boolean approved; public Transaction(String id, double amount) { this.id = id; this.amount = amount; this.approved = false; } @Override public String toString() { return id + ": $" + amount + " (" + (approved ? "✓" : "✗") + ")"; }}public class DataProcessing { public static void main(String[] args) { // Create transaction list List<Transaction> transactions = new ArrayList<>(); transactions.add(new Transaction("TXN001", 150.00)); transactions.add(new Transaction("TXN002", 25.50)); transactions.add(new Transaction("TXN003", 999.99)); transactions.add(new Transaction("TXN004", 10.00)); transactions.add(new Transaction("TXN005", 500.00)); System.out.println("=== Transaction Processing System ===\n"); System.out.println("All transactions:"); transactions.forEach(t -> System.out.println(" " + t)); // ===== PROCESS 1: Approve transactions over $100 ===== System.out.println("\n--- Approving transactions over $100 ---"); Iterator<Transaction> iterator = transactions.iterator(); int approvedCount = 0; while (iterator.hasNext()) { Transaction txn = iterator.next(); if (txn.amount > 100) { txn.approved = true; approvedCount++; System.out.println("Approved: " + txn.id + " ($" + txn.amount + ")"); } } System.out.println("Total approved: " + approvedCount); // ===== PROCESS 2: Remove transactions under $20 ===== System.out.println("\n--- Removing small transactions (< $20) ---"); iterator = transactions.iterator(); int removedCount = 0; while (iterator.hasNext()) { Transaction txn = iterator.next(); if (txn.amount < 20) { System.out.println("Removing: " + txn.id + " ($" + txn.amount + ")"); iterator.remove(); removedCount++; } } System.out.println("Total removed: " + removedCount); // ===== PROCESS 3: Calculate total of approved transactions ===== System.out.println("\n--- Calculating totals ---"); double totalApproved = 0; double totalPending = 0; for (Transaction txn : transactions) { if (txn.approved) { totalApproved += txn.amount; } else { totalPending += txn.amount; } } System.out.println("Total approved: $" + totalApproved); System.out.println("Total pending: $" + totalPending); // ===== FINAL STATE ===== System.out.println("\n=== Final Transaction List ==="); ListIterator<Transaction> listIter = transactions.listIterator(); while (listIter.hasNext()) { int index = listIter.nextIndex(); Transaction txn = listIter.next(); System.out.println((index + 1) + ". " + txn); } System.out.println("\nTotal remaining: " + transactions.size()); }}🔑 Key Concepts
hasNext()
Checks if there are more elements to iterate
while (iterator.hasNext()) - safe way to loop
next()
Returns the next element and moves the cursor forward
Must call hasNext() first to avoid exception
remove()
Safely removes the last element returned by next()
Only way to remove during iteration without ConcurrentModificationException
Fail-Fast Behavior
Iterator detects if collection was modified during iteration
Throws ConcurrentModificationException if collection changes
✨ Best Practices
- ✓Use enhanced for-loop when you just need to read elements
- ✓Use Iterator when you need to remove elements during iteration
- ✓Use ListIterator when you need bidirectional traversal or modification
- ✓Always call hasNext() before calling next() to avoid NoSuchElementException
- ✓Don't modify collection directly during iteration (use iterator.remove() instead)
- ✓Use forEach() with lambdas for functional-style code
- ✓Remember that Iterator can only be used once - create new one for re-iteration
💼 Interview Tips
- •Iterator is fail-fast - throws ConcurrentModificationException if collection modified
- •ListIterator has previousIndex(), nextIndex(), set(), and add() methods
- •Enhanced for-loop is syntactic sugar for Iterator
- •You cannot go back with regular Iterator (use ListIterator for bidirectional)
- •remove() can only be called once per call to next()
- •Understand the difference between Iterator.remove() vs Collection.remove()
- •Know that forEach() doesn't allow removing elements
- •Spliterator is for parallel processing (advanced topic)