How Iterators Work in Java Collections?
Understanding Traversal Mechanisms and Internal Implementation
Introduction
Iteration is a fundamental operation in programming — it allows us to traverse elements in a collection one by one.
In Java Collections Framework, an Iterator is an object that enables traversing a collection, removing elements safely during iteration, and providing a uniform way to access data regardless of the underlying collection type.
In this blog, we will:
- Learn about the Iterator interface.
- Understand different types of iterators.
- Explore the internal working of iterators.
- Learn how to implement a custom iterator.
- Understand the advantages and limitations of iterators.
1. What is an Iterator in Java?
The Iterator interface belongs to the java.util package and provides methods to traverse a collection.
Core Methods of Iterator
Method | Description |
---|---|
hasNext() | Returns true if the iteration has more elements. |
next() | Returns the next element in the iteration. |
remove() | Removes the last element returned by this iterator (optional operation). |
Example:
import java.util.*;
public class IteratorExample {
public static void main(String[] args) {
List list = Arrays.asList("Java", "Python", "C++");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
2. Types of Iterators in Java
Java provides several kinds of iterators depending on the collection type:
2.1 Iterator
- Works for all collection types (List, Set, etc.).
- Supports hasNext(), next(), and remove().
2.2 ListIterator
- Extends Iterator.
- Works only for List collections.
- Supports bi-directional traversal.
- Provides previous(), hasPrevious(), add(), and set() methods.
2.3 Spliterator
- Introduced in Java 8.
- Supports parallel traversal.
- Provides methods like tryAdvance() and forEachRemaining().
3. How Iterators Work Internally
When an iterator is requested from a collection (e.g., list.iterator()), Java returns an iterator object that:
- Holds a reference to the collection.
- Maintains a cursor pointing to the current element.
- Tracks the collection’s modification count to detect concurrent modifications.
Fail-Fast Behavior
Most iterators in Java are fail-fast.
This means they throw a ConcurrentModificationException if the collection is modified while iterating (except through the iterator’s own remove method).
Example:
List list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator it = list.iterator();
list.add("D"); // modifies collection
it.next(); // throws ConcurrentModificationException
Step-by-Step: Implementing a Custom Iterator
Step 1 — Make CustomArrayList Iterable
To allow iteration with a for-each loop, we implement Iterable.
import java.util.Iterator;
public class CustomArrayList implements Iterable {
private Object[] data;
private int size;
private int capacity;
public CustomArrayList() {
capacity = 10;
data = new Object[capacity];
size = 0;
}
}
Here, Iterable requires implementing the method:
@Override
public Iterator iterator() {
return new CustomIterator();
}
Step 2 — Create the CustomIterator Class
We create an inner class inside CustomArrayList implementing Iterator.
private class CustomIterator implements Iterator {
private int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < size;
}
@Override
public T next() {
if (!hasNext()) throw new IndexOutOfBoundsException();
return (T) data[currentIndex++];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
- hasNext() → returns true if more elements exist.
- next() → returns the current element and moves the index forward.
- remove() → optional, so we throw UnsupportedOperationException.
Step 3 — Add Elements and Support Iteration
We must ensure CustomArrayList supports adding elements and storing them.
public void add(T element) {
if (size == capacity) grow();
data[size++] = element;
}
private void grow() {
capacity = capacity + (capacity >> 1); // Increase by 50%
Object[] newData = new Object[capacity];
System.arraycopy(data, 0, newData, 0, size);
data = newData;
}
Step 4 — Add Utility Methods
We add get(), size(), and display().
@SuppressWarnings("unchecked")
public T get(int index) {
if (index >= size || index < 0) throw new IndexOutOfBoundsException();
return (T) data[index];
}
public int size() {
return size;
}
public void display() {
for (int i = 0; i < size; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
Step 5 — Test the Custom Iterator
public class TestCustomIterator {
public static void main(String[] args) {
CustomArrayList list = new CustomArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
for (String item : list) {
System.out.println(item);
}
}
}
Expected Output:
Java
Python
C++
Final Complete Code
import java.util.Iterator;
public class CustomArrayList implements Iterable {
private Object[] data;
private int size;
private int capacity;
public CustomArrayList() {
capacity = 10;
data = new Object[capacity];
size = 0;
}
public void add(T element) {
if (size == capacity) grow();
data[size++] = element;
}
private void grow() {
capacity = capacity + (capacity >> 1); // Increase by 50%
Object[] newData = new Object[capacity];
System.arraycopy(data, 0, newData, 0, size);
data = newData;
}
@SuppressWarnings("unchecked")
public T get(int index) {
if (index >= size || index < 0) throw new IndexOutOfBoundsException();
return (T) data[index];
}
public int size() {
return size;
}
public void display() {
for (int i = 0; i < size; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
@Override
public Iterator iterator() {
return new CustomIterator();
}
private class CustomIterator implements Iterator {
private int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < size;
}
@Override
public T next() {
if (!hasNext()) throw new IndexOutOfBoundsException();
return (T) data[currentIndex++];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
Test Code
public class TestCustomIterator {
public static void main(String[] args) {
CustomArrayList list = new CustomArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
for (String item : list) {
System.out.println(item);
}
}
}
5. How Iterators Differ from For-Loops
Feature | Iterator | For-Loop |
---|---|---|
Type of Traversal | Explicit | Implicit |
Removal of Elements | Supported via remove() | Not directly possible |
Fail-Fast Behavior | Yes (ConcurrentModificationException) | No |
Flexibility | Works for all collections | Works with indexed collections only |
6. Advantages of Iterators
- Uniform Traversal: Works for any collection type.
- Safe Element Removal: Supports safe removal during iteration.
- Encapsulation: Hides internal structure of collections.
- Enhanced For Loop Support: Enables cleaner syntax with Iterable.
7. Limitations of Iterators
- Fail-Fast Behavior: Throws exceptions if collection is modified outside iterator.
- No Random Access: Must traverse sequentially.
- Remove Only Supported for Certain Iterators: Not all iterators allow element removal.
8. Real-World Use Cases
- Traversing large collections efficiently.
- Implementing algorithms that process collections sequentially.
- Removing elements from collections safely during iteration.
- Parallel processing with Spliterator in Java 8+.
9. Advanced Iterators — Spliterator
Introduced in Java 8, Spliterator allows parallel processing by splitting collections into smaller parts.
Example:
List list = Arrays.asList("A", "B", "C", "D");
Spliterator spliterator = list.spliterator();
spliterator.forEachRemaining(System.out::println);
Conclusion
In this blog, we’ve:
- Learned what iterators are and how they work internally.
- Explored the different types of iterators in Java.
- Built a custom iterator for a dynamic array.
- Understood advantages, limitations, and use cases.
Iterators are a core part of Java Collections Framework, allowing uniform and safe traversal across different collection types.
Understanding iterators deeply empowers developers to work efficiently with Java collections, especially when dealing with large datasets.
Next Blog- ConcurrentHashMap Internal Working