Last Updated on July 23, 2024

Java Iterators are implementation of Iterator interface that defines a protocol for iterating over a collection of elements. It provides a standardized way to access elements in a collection one at a time, without exposing the underlying implementation details of the collection.

Before we dive into iterator lets see what Collection framework is on which Iterator operates.

Collection Framework

The Java Collection Framework (JCF) is a comprehensive set of classes and interfaces that provide a unified architecture for storing, retrieving, manipulating, and iterating over collections of objects in Java. 

The core of JCF lies in interfaces, which define a set of methods that a collection class must implement. These interfaces specify the operations that can be performed on a collection, promoting consistency and reusability. Common interfaces include Collection, List, Set, Map, and Iterable (parent interface for iterators).

JCF provides concrete classes that implement these interfaces, offering various data structures with specific functionalities. Examples include ArrayList (dynamically resizing array-based list), LinkedList (doubly-linked list for efficient insertions and deletions), HashSet (unordered collection of unique elements), TreeSet (sorted set), and HashMap (key-value pair storage).

Iterable Interface

The Iterable interface is the root interface for all the collection classes.

Iterable is a marker interface. It doesn’t define any specific methods for iteration itself.

By implementing Iterable, a class essentially promises it can provide an Iterator object when needed for iterating over its elements.

This interface applies not just to core JCF collections (like ArrayList, HashMap, etc.) but can also be implemented by custom classes outside the JCF.

Collection Interface

The Collection interface in Java is the foundation of the Java Collections Framework (JCF). It acts as a blueprint that defines the core functionalities expected from a collection of objects.

Some of the methods of Collection interface are Boolean add ( Object obj), Boolean addAll ( Collection c), void clear(), etc. which are implemented by all the subclasses of Collection interface.

Iterator Interface

Java Iterator interface, part of the package java.util,  provides a way to traverse through a collection of elements sequentially.

​​It allows you to iterate through a collection without exposing the underlying structure of the collection.

The Iterator interface specifies three main methods that facilitate iteration over collections:

hasNext()

The hasNext() method in Java’s Iterator interface checks if more elements exist in the iteration. It returns a boolean without moving the iterator, typically used in loops to safely control traversal of collections without exceptions.

next()

The next() method in Java’s Iterator interface returns the next element in the iteration and advances the iterator’s position. It throws NoSuchElementException if no more elements exist. Used to retrieve elements sequentially from collections.

remove()

The remove() method in Java’s Iterator interface deletes the last element returned by next(). It’s optional, throws UnsupportedOperationException if not implemented, and IllegalStateException if called improperly. Use cautiously to modify collections during iteration.

Java Iterator Example

One of the primary advantages of using Java Iterators is the ability to safely modify collections during iteration.

Example:

import java.util.*;

public class IteratorModificationExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date", "elderberry"));
        
        Iterator<String> iterator = fruits.iterator();
        while (iterator.hasNext()) {
            String fruit = iterator.next();
            if (fruit.startsWith("b") || fruit.startsWith("d")) {
                iterator.remove();
            }
        }
        
        System.out.println("Remaining fruits: " + fruits);
    }
}

Above code safely removes elements from the collection during iteration, which is not possible with a standard for-each loop or indexed for loop without risking a ConcurrentModificationException.

The output is:

Remaining fruits: [apple, cherry, elderberry]

Java Custom Iterator

There are several reasons why would we need a custom iterator.

Conditional Iteration: You might need to iterate over a collection based on specific criteria. A custom iterator can implement logic within hasNext() or next() to only return elements that meet certain conditions.

Modified Iteration Order: The default iteration order of a collection might not be suitable for your needs. A custom iterator can modify the order in which elements are returned by overriding the next() method.

Partial Iteration: You might only be interested in iterating over a subset of elements in a collection. A custom iterator can limit the iteration based on specific conditions.

Here’s an example of a custom iterator in Java that iterates over an ArrayList but skips every other element.

package org.codeline;

import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

public class EvenNumberIterator implements Iterator<Integer> {

private final List<Integer> numbers;
private int currentIndex = -1;

public EvenNumberIterator(List<Integer> numbers) {
this.numbers = numbers;
}

@Override
public boolean hasNext() {
// Find the next even element from the current position
for (int i = currentIndex + 1; i < numbers.size(); i++) {
if (numbers.get(i) % 2 == 0) {
return true;
}
}
return false;
}

@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
// Move to the next even element
do {
currentIndex++;
} while (currentIndex < numbers.size() && numbers.get(currentIndex) % 2 != 0);
return numbers.get(currentIndex);
}
}

Main program:

package org.codeline;

import java.util.ArrayList;
import java.util.Iterator;

public class Main {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(3);
numbers.add(5);
numbers.add(7);
numbers.add(2);
numbers.add(4);

Iterator<Integer> iterator = new EvenNumberIterator(numbers);

System.out.println("Even numbers: ");
while (iterator.hasNext()) {
int number = iterator.next();
System.out.print(number + " ");
}
}
}

Output:

Even numbers: 2 4 

Iterator Alternatives

Java provides several alternatives to the traditional Iterator for traversing and manipulating collections, each with its own strengths and use cases.

These alternatives offer developers flexibility in how they approach collection processing, often providing more concise syntax or additional functionality.

ListIterator

For more advanced list traversal, the ListIterator interface extends the capabilities of the standard Iterator. It supports bidirectional traversal and element insertion, making it particularly useful for linked lists or when backward iteration is needed. However, its additional complexity means it’s often overlooked for simpler use cases.

Enhanced for loop

Introduced in Java 5, it offers a clean and readable syntax for iterating over collections and arrays. Its syntax simplifies the process of traversing a collection, eliminating the need for explicit iterator creation and management.

However, it’s important to note that while the for-each loop excels in readability, it doesn’t allow for modification of the collection during iteration, and it doesn’t provide access to the index of elements in indexed collections.

Stream API

Java 8 introduced the Stream API, which represents a more functional approach to collection processing.

Streams allow for a series of aggregate operations to be performed on collections, often resulting in more expressive and concise code. The Stream API supports both sequential and parallel processing, making it easier to leverage multi-core architectures.

Operations like map, filter, and reduce can be chained together, enabling powerful data transformations with minimal boilerplate code.

forEach() method

Also introduced in Java 8, the forEach() method is available directly on collection interfaces. This method accepts a Consumer functional interface, allowing for concise iteration over collection elements. It’s particularly useful for simple operations where the full power of streams isn’t necessary.

Conclusion

The traditional Iterator remains a fundamental concept, offering fine-grained control over collection traversal.

Custom Iterator implementation gives us flexibility for conditional iteration and modified iteration order over collection.

In practice, modern Java development often combines these approaches, using the most appropriate tool for each specific situation. This flexibility allows developers to write cleaner, more efficient, and more expressive code when working with collections.

Scroll to Top