Java Iterator

Universal way to traverse collections safely and efficiently

🔄 What is Iterator?

Iterator is a Java interface that provides a universal way to traverse collections. It allows safe element removal during iteration and works with all collection types consistently.


// Simple Iterator example
List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry");
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}
                                    

Iterator Key Features

🔄

Universal Traversal

Works with all collection types

List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
// Both use same iterator pattern
Iterator<String> listIt = list.iterator();
Iterator<String> setIt = set.iterator();
🛡️

Safe Removal

Remove elements safely during iteration

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String item = it.next();
    if (item.equals("remove")) {
        it.remove(); // Safe removal
    }
}

Fail-Fast

Detects concurrent modifications

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String item = it.next();
    // list.add("new"); // ConcurrentModificationException
    it.remove(); // This is safe
}
🎯

Forward-Only

One-way traversal from start to end

Iterator<String> it = list.iterator();
// Can only move forward
while (it.hasNext()) {
    String current = it.next();
    // No previous() method in basic Iterator
}

🔹 Basic Iterator Usage

Essential Iterator operations and patterns:

import java.util.*;

public class IteratorExample {
    public static void main(String[] args) {
        // Create a list with some data
        List<String> languages = new ArrayList<>();
        languages.add("Java");
        languages.add("Python");
        languages.add("JavaScript");
        languages.add("C++");
        languages.add("Go");
        
        // Basic iteration
        System.out.println("All languages:");
        Iterator<String> iterator = languages.iterator();
        while (iterator.hasNext()) {
            String language = iterator.next();
            System.out.println("- " + language);
        }
        
        // Iteration with conditional removal
        System.out.println("\nRemoving languages with '+' in name:");
        iterator = languages.iterator(); // Get fresh iterator
        while (iterator.hasNext()) {
            String language = iterator.next();
            if (language.contains("+")) {
                System.out.println("Removing: " + language);
                iterator.remove(); // Safe removal during iteration
            }
        }
        
        System.out.println("\nRemaining languages: " + languages);
        
        // Enhanced for-loop (uses iterator internally)
        System.out.println("\nUsing enhanced for-loop:");
        for (String language : languages) {
            System.out.println("* " + language);
        }
    }
}

Output:

All languages:

- Java

- Python

- JavaScript

- C++

- Go


Removing languages with '+' in name:

Removing: C++


Remaining languages: [Java, Python, JavaScript, Go]


Using enhanced for-loop:

* Java

* Python

* JavaScript

* Go

🔹 Iterator with Different Collections

Using Iterator with various collection types:

// ArrayList Iterator
List<Integer> arrayList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
System.out.println("ArrayList iteration:");
Iterator<Integer> listIt = arrayList.iterator();
while (listIt.hasNext()) {
    Integer num = listIt.next();
    if (num % 2 == 0) {
        listIt.remove(); // Remove even numbers
    } else {
        System.out.println("Odd number: " + num);
    }
}
System.out.println("ArrayList after removal: " + arrayList);

// HashSet Iterator
Set<String> hashSet = new HashSet<>(Arrays.asList("Apple", "Banana", "Cherry", "Date"));
System.out.println("\nHashSet iteration:");
Iterator<String> setIt = hashSet.iterator();
while (setIt.hasNext()) {
    String fruit = setIt.next();
    System.out.println("Fruit: " + fruit);
    if (fruit.startsWith("B")) {
        setIt.remove(); // Remove fruits starting with 'B'
    }
}
System.out.println("HashSet after removal: " + hashSet);

// TreeMap Iterator (for keys)
TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Charlie", 25);
treeMap.put("Alice", 30);
treeMap.put("Bob", 22);

System.out.println("\nTreeMap key iteration (sorted):");
Iterator<String> keyIt = treeMap.keySet().iterator();
while (keyIt.hasNext()) {
    String name = keyIt.next();
    Integer age = treeMap.get(name);
    System.out.println(name + " is " + age + " years old");
}

// TreeMap entry iteration
System.out.println("\nTreeMap entry iteration:");
Iterator<Map.Entry<String, Integer>> entryIt = treeMap.entrySet().iterator();
while (entryIt.hasNext()) {
    Map.Entry<String, Integer> entry = entryIt.next();
    if (entry.getValue() < 25) {
        System.out.println("Removing young person: " + entry.getKey());
        entryIt.remove();
    }
}
System.out.println("TreeMap after removal: " + treeMap);

Output:

ArrayList iteration:

Odd number: 1

Odd number: 3

Odd number: 5

ArrayList after removal: [1, 3, 5]


HashSet iteration:

Fruit: Apple

Fruit: Cherry

Fruit: Date

Fruit: Banana

HashSet after removal: [Apple, Cherry, Date]


TreeMap key iteration (sorted):

Alice is 30 years old

Bob is 22 years old

Charlie is 25 years old

🔹 ListIterator - Bidirectional Iterator

ListIterator provides additional functionality for Lists:

List<String> cities = new ArrayList<>(Arrays.asList("New York", "London", "Tokyo", "Paris"));

// ListIterator allows bidirectional traversal
ListIterator<String> listIterator = cities.listIterator();

System.out.println("Forward iteration:");
while (listIterator.hasNext()) {
    int index = listIterator.nextIndex();
    String city = listIterator.next();
    System.out.println(index + ": " + city);
    
    // Add element after specific city
    if (city.equals("London")) {
        listIterator.add("Berlin"); // Insert after London
    }
    
    // Replace specific city
    if (city.equals("Tokyo")) {
        listIterator.set("Kyoto"); // Replace Tokyo with Kyoto
    }
}

System.out.println("\nList after modifications: " + cities);

System.out.println("\nBackward iteration:");
while (listIterator.hasPrevious()) {
    int index = listIterator.previousIndex();
    String city = listIterator.previous();
    System.out.println(index + ": " + city);
}

// Start from specific position
System.out.println("\nIteration starting from index 2:");
ListIterator<String> positionIterator = cities.listIterator(2);
while (positionIterator.hasNext()) {
    String city = positionIterator.next();
    System.out.println("From position 2: " + city);
}

// Demonstrate ListIterator methods
List<Integer> numbers = new ArrayList<>(Arrays.asList(10, 20, 30));
ListIterator<Integer> numIt = numbers.listIterator();

System.out.println("\nListIterator methods demonstration:");
System.out.println("Original: " + numbers);

// Add at beginning
numIt.add(5);
System.out.println("After adding 5 at start: " + numbers);

// Move to next and replace
numIt.next(); // Skip 5
numIt.set(15); // Replace 10 with 15
System.out.println("After replacing 10 with 15: " + numbers);

// Add between elements
numIt.next(); // Move to 20
numIt.add(25); // Add 25 before 20
System.out.println("After adding 25: " + numbers);

Output:

Forward iteration:

0: New York

1: London

3: Tokyo

5: Paris


List after modifications: [New York, London, Berlin, Kyoto, Paris]


Backward iteration:

4: Paris

3: Kyoto

2: Berlin

1: London

0: New York


ListIterator methods demonstration:

Original: [10, 20, 30]

After adding 5 at start: [5, 10, 20, 30]

After replacing 10 with 15: [5, 15, 20, 30]

After adding 25: [5, 15, 25, 20, 30]

🔹 Iterator Best Practices

Common patterns and best practices for using Iterator:

// 1. Always check hasNext() before calling next()
List<String> items = Arrays.asList("A", "B", "C");
Iterator<String> it = items.iterator();
while (it.hasNext()) { // Always check first
    String item = it.next(); // Then get next
    System.out.println(item);
}

// 2. Use try-with-resources for custom iterators (if they implement AutoCloseable)
// Most built-in iterators don't need this, but custom ones might

// 3. Avoid ConcurrentModificationException
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
Iterator<String> iterator = list.iterator();

// WRONG - Don't modify collection directly during iteration
// while (iterator.hasNext()) {
//     String item = iterator.next();
//     if (item.equals("B")) {
//         list.remove(item); // ConcurrentModificationException!
//     }
// }

// RIGHT - Use iterator.remove()
iterator = list.iterator(); // Get fresh iterator
while (iterator.hasNext()) {
    String item = iterator.next();
    if (item.equals("B")) {
        iterator.remove(); // Safe removal
    }
}
System.out.println("After safe removal: " + list);

// 4. Iterator vs Enhanced For-Loop
System.out.println("\nComparison of iteration methods:");

// Enhanced for-loop (cleaner for simple iteration)
for (String item : list) {
    System.out.println("Enhanced for: " + item);
}

// Iterator (better when you need to remove elements)
iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    System.out.println("Iterator: " + item);
    // Can call iterator.remove() here if needed
}

// 5. Custom Iterator example
class NumberRange implements Iterable<Integer> {
    private int start, end;
    
    NumberRange(int start, int end) {
        this.start = start;
        this.end = end;
    }
    
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int current = start;
            
            @Override
            public boolean hasNext() {
                return current <= end;
            }
            
            @Override
            public Integer next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                return current++;
            }
        };
    }
}

// Use custom iterator
System.out.println("\nCustom iterator (range 1-5):");
NumberRange range = new NumberRange(1, 5);
for (Integer num : range) {
    System.out.println("Number: " + num);
}

Output:

A

B

C

After safe removal: [A, C, D]


Comparison of iteration methods:

Enhanced for: A

Enhanced for: C

Enhanced for: D

Iterator: A

Iterator: C

Iterator: D


Custom iterator (range 1-5):

Number: 1

Number: 2

Number: 3

Number: 4

Number: 5

🧠 Test Your Knowledge

What happens if you modify a collection directly while iterating with Iterator?