Appearance
Iterator
Java's collection classes can use the for-each loop. Lists, Sets, and Queues iterate through each element, while Maps iterate through each key. For example, with a List:
java
List<String> list = List.of("Apple", "Orange", "Pear");
for (String s : list) {
System.out.println(s);
}
In reality, the Java compiler does not know how to iterate over a List. The above code compiles successfully simply because the compiler rewrites the for-each loop into a standard for loop using an Iterator
:
java
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
This pattern of iterating through a collection using an Iterator
object is known as the iterator.
The benefit of using an iterator is that the caller can always iterate over various collection types in a uniform manner without needing to be concerned about their internal storage structures.
For instance, we know that ArrayList
stores elements in an array and also provides a get(int)
method. While we could iterate using a for loop:
java
for (int i = 0; i < list.size(); i++) {
Object value = list.get(i);
}
This approach requires the caller to know the internal storage structure of the collection. Moreover, if we were to replace ArrayList
with LinkedList
, the get(int)
method would take longer as the index increases. If we switched from ArrayList
to Set
, the above code would fail to compile since a Set
does not have indices.
Using an iterator avoids these issues because the Iterator
object is created internally by the collection object itself. It knows how to efficiently traverse the internal data collection, allowing the caller to write uniform code. The compiler can then automatically convert the standard for-each loop into an iterator traversal.
If we want to write our own collection class that supports the for-each loop, we just need to meet the following conditions:
- The collection class implements the
Iterable
interface, which requires returning anIterator
object. - Use the
Iterator
object to iterate over the internal data of the collection.
The key here is that the collection class returns an Iterator
object by calling the iterator()
method, and this object must know how to traverse that collection.
Here’s a simple example of an Iterator
that always traverses the collection in reverse order:
java
// Iterator
import java.util.*;
public class Main {
public static void main(String[] args) {
ReverseList<String> rlist = new ReverseList<>();
rlist.add("Apple");
rlist.add("Orange");
rlist.add("Pear");
for (String s : rlist) {
System.out.println(s);
}
}
}
class ReverseList<T> implements Iterable<T> {
private List<T> list = new ArrayList<>();
public void add(T t) {
list.add(t);
}
@Override
public Iterator<T> iterator() {
return new ReverseIterator(list.size());
}
class ReverseIterator implements Iterator<T> {
int index;
ReverseIterator(int index) {
this.index = index;
}
@Override
public boolean hasNext() {
return index > 0;
}
@Override
public T next() {
index--;
return ReverseList.this.list.get(index);
}
}
}
Although the implementations of ReverseList
and ReverseIterator
are somewhat complex, it is important to note that this is a one-time effort for the underlying collection library. The caller can write code using the for-each loop without needing to know the internal storage and traversal logic of the collection.
When implementing an Iterator
, we can often use an inner class to implement the Iterator
interface. This inner class can directly access all fields and methods of the corresponding outer class. For example, in the above code, the inner class ReverseIterator
can use ReverseList.this
to obtain the current outer class's this
reference, allowing it to access all fields and methods of ReverseList
.
Summary
The Iterator
is an abstract data access model. The benefits of using the iterator pattern for iteration include:
- A uniform access model for any collection.
- The caller is unaware of the internal structure of the collection.
- The
Iterator
object returned by the collection class knows how to iterate.
Java provides a standard iterator model, where collection classes implement the java.util.Iterable
interface and return instances of java.util.Iterator
.