Iterator Design Pattern¶
The Iterator pattern is a behavioral design pattern that allows sequential access to elements of a collection without exposing its underlying structure. The goal is to provide a way to access the elements of an aggregate object (such as an array, list, or set) one by one, without needing to understand how the collection is implemented.
What ?¶
Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.).
Key Components
- Iterator: An interface that defines methods for traversing elements (e.g.,
hasNext()
,next()
). - Concrete Iterator: A class implementing the
Iterator
interface for specific collections. - Aggregate: An interface that defines a method to return an iterator (e.g.,
iterator()
). - Concrete Aggregate: A collection (like List, Set, or Custom Collection) that returns a concrete iterator for traversal.
When to Use ?¶
- When you need to traverse a collection without exposing its internal structure.
- When the collection might be complex (e.g., trees, graphs) or needs to be accessed in multiple ways.
- When you want to ensure uniform traversal logic regardless of the type of collection (array, list, set, etc.).
- Helpful when designing read-only views over complex collections.
- If you need multiple ways to traverse the collection (e.g., forward, reverse, or filtered).
When Not to Use ?¶
- If the collection is small and performance is critical, an iterator might introduce overhead.
- When direct access to elements is more efficient or easier (e.g., in simple arrays or lists).
Advantages¶
- Separation of concerns the client doesn’t need to know the internal structure of the collection.
- Multiple traversal strategies can implement different types of iterators (e.g., reverse iterator).
- Simplifies collections makes it easier to work with complex structures like trees or graphs.
DisAdvantages¶
- Performance overhead if not managed properly, iterators can add unnecessary overhead.
- Limited control over collection Iterators typically provide read-only access.
- Concurrency issues Modifying a collection while iterating over it may throw
ConcurrentModificationException
.
How to Implement ?¶
Sample Java Example
Let's go through with a simple Java example of a custom iterator for a list of strings.
class NameIterator implements Iterator<String> {
private String[] names;
private int index;
public NameIterator(String[] names) {
this.names = names;
}
@Override
public boolean hasNext() {
return index < names.length;
}
@Override
public String next() {
if (this.hasNext()) {
return names[index++];
}
return null;
}
}
interface NameCollection {
Iterator<String> getIterator();
}
class NameRepository implements NameCollection {
private String[] names = {"John", "Alice", "Robert", "Michael"};
@Override
public Iterator<String> getIterator() {
return new NameIterator(names);
}
}
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();
Iterator<String> iterator = namesRepository.getIterator();
while (iterator.hasNext()) {
String name = iterator.next();
System.out.println("Name: " + name);
}
}
}
Using Java’s Built-in Iterators
Java already provides built-in iterators for its collection framework (Iterator
, ListIterator
, and Spliterator
).
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class BuiltInIteratorExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("John");
names.add("Alice");
names.add("Robert");
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
System.out.println("Name: " + iterator.next());
}
}
}
Spring Boot Example
Spring Boot doesn’t explicitly use the Iterator pattern as part of its core framework, but it does utilize Iterators internally (e.g., when dealing with ApplicationContext
beans or data access layers). You can integrate the Iterator pattern within Spring Boot to handle collections of objects such as configurations, database entities, or APIs.
public class Book {
private String title;
public Book(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
import java.util.ArrayList;
import java.util.List;
public class BookRepository {
private List<Book> books = new ArrayList<>();
public BookRepository() {
books.add(new Book("Spring in Action"));
books.add(new Book("Java 8 in Action"));
books.add(new Book("Microservices Patterns"));
}
public Iterator<Book> getIterator() {
return books.iterator();
}
}
import org.springframework.stereotype.Service;
import java.util.Iterator;
@Service
public class BookService {
private final BookRepository bookRepository = new BookRepository();
public void printBooks() {
Iterator<Book> iterator = bookRepository.getIterator();
while (iterator.hasNext()) {
Book book = iterator.next();
System.out.println("Book: " + book.getTitle());
}
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping
public void listBooks() {
bookService.printBooks();
}
}
When you access http://localhost:8080/books
, it will print the list of books using the iterator after running the application
Summary¶
The Iterator pattern is a powerful way to decouple iteration logic from collections, ensuring a clean separation of concerns. It’s useful in Java projects where complex or multiple ways of traversal are required. When integrated into Spring Boot, the pattern can be applied to iterate over configurations, data models, or APIs efficiently.