Iterator
The Iterator pattern is a behavioral design pattern that provides a way to access the elements of an aggregate object (like a list, set, or tree) sequentially without exposing its underlying representation. It defines a standard interface for creating iterators, allowing clients to traverse and manipulate elements without knowing how the aggregate is organized.
This pattern decouples the algorithms for traversing a collection from the collection itself. It promotes loose coupling between the collection and the code that uses it, making the collection more flexible and easier to maintain. Iterators also allow for multiple traversals of the same collection concurrently.
Usage
The Iterator pattern is commonly used in scenarios where:
- You need to provide a way to access the elements of a collection without exposing its internal structure.
- You want to support multiple traversal modes for a collection (e.g., forward, backward, skipping elements).
- You need to iterate over complex data structures like trees or graphs.
- You want to allow clients to iterate over a collection while it is being modified (with appropriate synchronization).
Examples
-
Java Collections Framework: The
java.util.Iteratorinterface and its implementations (e.g.,ListIterator,Iterator<E>for various collection types likeArrayList,LinkedList,HashSet) are a prime example. You can iterate over these collections using afor-eachloop, which internally utilizes the iterator pattern. -
Python Generators: Python’s generators, created using the
yieldkeyword, effectively implement the iterator pattern. A generator function returns an iterator object that produces values on demand, rather than storing the entire sequence in memory. This is particularly useful for large datasets. -
C++ Standard Template Library (STL): C++ provides iterators as a core part of its STL. Different container types (e.g.,
vector,list,map) have their own iterator classes, allowing you to traverse their elements in a generic way. Iterators in C++ are more powerful than in Java, supporting various operations like increment, decrement, dereferencing, and comparison.
Specimens
15 implementationsThe Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It defines a standard interface for creating iterators and iterating over collections. This implementation uses Dart’s Iterable and Iterator classes. The Album class represents the aggregate, and the AlbumIterator class provides the iteration logic. Dart’s yield keyword makes creating iterators concise, and using an Iterable allows the Album class to be used with other Dart collection utilities easily. This embraces the functional aspects of Dart and utilizes its built-in iteration support.
// Define the iterator class
class AlbumIterator<T> implements Iterator<T> {
final List<T> _data;
int _currentIndex = 0;
AlbumIterator(this._data);
@override
T get current => _data[_currentIndex];
@override
bool moveNext() {
return _currentIndex < _data.length - 1;
}
}
// Define the aggregate class (collection)
class Album<T> extends Iterable<T> {
final List<T> _songs;
Album(this._songs);
@override
Iterator<T> get iterator => AlbumIterator(_songs);
void addSong(T song) {
_songs.add(song);
}
}
void main() {
final album = Album<String>(['Song 1', 'Song 2', 'Song 3']);
for (final song in album) {
print(song);
}
final iterator = album.iterator;
while (iterator.moveNext()) {
print('Current song using iterator: ${iterator.current}');
}
}
The Iterator pattern is a behavioral design pattern that provides sequential access to a collection’s elements without exposing its underlying representation. This allows traversing complex data structures without needing to know their internal workings. In Scala, the Iterator trait is built-in and extensively used, especially with collections. My example demonstrates a custom iterator for a sequence of Fibonacci numbers. It implements Iterator[Int] by maintaining the current and next values and updating them in the next() method. Using a trait and the next()/hasNext() methods is the standard Scala way to define an Iterator, seamlessly integrating with for comprehensions and other functional constructs.
trait FibonacciIterator extends Iterator[Int] {
private var current: Int = 0
private var nextVal: Int = 1
def hasNext: Boolean = true
def next(): Int = {
val result = current
val temp = current + nextVal
current = nextVal
nextVal = temp
result
}
}
object FibonacciSequence {
def iterator: FibonacciIterator = new FibonacciIterator()
def take(n: Int): Seq[Int] = iterator.take(n).toSeq
def main(args: Array[String]): Unit = {
println(take(10)) // Output: Seq(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
//Using a for comprehension
println("First 5 Fibonacci numbers:")
for (fib <- iterator.take(5)) {
println(fib)
}
}
}
The Iterator pattern provides a way to access the elements of an aggregate object (like an array or list) sequentially without exposing its underlying representation. It defines a standard interface for traversing collections, enabling clients to interact with different data structures in a uniform manner.
The code implements this by defining a BookCollection class that holds an array of books. Instead of directly accessing the array, the BookCollection returns a BookIterator object. This iterator implements the Iterator interface, providing current(), next(), key(), valid(), and rewind() methods to control traversal. This approach adheres to PHP’s object-oriented style, enhancing encapsulation and flexibility. Using interfaces is a standard practice in PHP for defining contracts.
<?php
// Iterator Interface
interface Iterator
{
public function current(): mixed;
public function next(): void;
public function key(): mixed;
public function valid(): bool;
public function rewind(): void;
}
// Aggregate (BookCollection)
class BookCollection implements Iterator
{
private array $books;
private int $position = 0;
public function __construct(array $books)
{
$this->books = $books;
}
public function current(): mixed
{
return $this->books[$this->position];
}
public function next(): void
{
$this->position++;
}
public function key(): mixed
{
return $this->position;
}
public function valid(): bool
{
return isset($this->books[$this->position]);
}
public function rewind(): void
{
$this->position = 0;
}
public function addBook(string $title, string $author): void
{
$this->books[] = ['title' => $title, 'author' => $author];
}
}
// Client Code
$books = new BookCollection([
['title' => 'The Lord of the Rings', 'author' => 'J.R.R. Tolkien'],
['title' => 'Pride and Prejudice', 'author' => 'Jane Austen'],
]);
foreach ($books as $book) {
echo "Title: " . $book['title'] . ", Author: " . $book['author'] . "\n";
}
?>
The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing the underlying representation. It defines a standard interface for iterating through objects, allowing you to traverse elements without knowing precisely how they are stored.
The Ruby implementation utilizes each – a core method that embodies the Iterator pattern. The MyCollection class encapsulates the data, and each yields each element to a block, acting as the iterator. This is exceptionally Ruby-like, as iteration through collections is primarily achieved with blocks and each, promoting a concise and readable style. We avoid explicit iterator classes as Ruby’s built-in iteration mechanisms fully realize the pattern’s intent.
# my_collection.rb
class MyCollection
def initialize(items)
@items = items
end
def each
@items.each do |item|
yield item
end
end
end
# Example Usage
collection = MyCollection.new([1, 2, 3, 4, 5])
puts "Iterating through the collection:"
collection.each do |item|
puts " Item: #{item}"
end
# Another example: collecting even numbers
even_numbers = []
collection.each do |item|
even_numbers << item if item.even?
end
puts "Even numbers: #{even_numbers}"
The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It defines an interface for traversing a collection, allowing you to add new iterators without modifying the collection itself. This example utilizes Swift’s built-in Sequence and Iterator protocols to create a custom iterator for a simple NumericSequence. This is very idiomatic Swift as protocols and protocol extensions are actively used for creating reusable and type-safe components. We implement makeIterator() to generate the iterator, and the iterator itself conforms to the IteratorProtocol providing a next() method.
// NumericSequence.swift
struct NumericSequence {
private let start: Int
private let end: Int
init(start: Int, end: Int) {
self.start = start
self.end = end
}
}
extension NumericSequence: Sequence {
func makeIterator() -> NumericIterator {
return NumericIterator(start: start, end: end)
}
}
struct NumericIterator: IteratorProtocol {
private var current: Int
private let end: Int
init(start: Int, end: Int) {
self.current = start
self.end = end
}
mutating func next() -> Int? {
guard current <= end else { return nil }
let value = current
current += 1
return value
}
}
// Example Usage:
let sequence = NumericSequence(start: 1, end: 5)
for number in sequence {
print(number)
}
The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing the underlying representation. It defines a standard interface for creating iterators, allowing clients to traverse a collection without knowing its specific implementation. This promotes loose coupling and flexibility.
In Kotlin, we leverage the built-in iterator() function available for all collections, which automatically provides an efficient and idiomatic implementation of the Iterator pattern. This example creates a simple TaskList class and uses Kotlin’s standard Iterator functionality to iterate through the tasks. The code directly utilizes for...in loops, which implicitly call the iterator() function, showcasing Kotlin’s concise and functional approach to iteration.
// TaskList.kt
data class Task(val description: String)
class TaskList {
private val tasks = mutableListOf<Task>()
fun addTask(task: Task) {
tasks.add(task)
}
fun getTasks(): List<Task> {
return tasks
}
// Kotlin's Iterable interface provides iterator() by default for List.
// No explicit Iterator implementation needed for basic iteration.
}
fun main() {
val taskList = TaskList()
taskList.addTask(Task("Grocery Shopping"))
taskList.addTask(Task("Laundry"))
taskList.addTask(Task("Code Review"))
println("Tasks:")
for (task in taskList.getTasks()) {
println("- ${task.description}")
}
println("\nUsing an explicit iterator:")
val iterator = taskList.getTasks().iterator()
while (iterator.hasNext()) {
val task = iterator.next()
println("- ${task.description}")
}
}
The Iterator pattern is a design pattern that provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It defines an interface for traversal and allows for multiple traversals without disrupting the underlying data.
The Rust code below demonstrates this through a custom Counter struct which implements the Iterator trait. This involves implementing next() which returns an Option<i32>, yielding the next value (if any) and updating the internal state. The use of Option is idiomatic for handling the potential end of the iteration. The Counter iterator produces a sequence of numbers from a starting point up to a defined limit. This implementation leverages Rust’s ownership and borrowing system, combined with its powerful trait system, to provide a safe and efficient iterator.
// src/main.rs
// A simple counter iterator
struct Counter {
current: i32,
max: i32,
}
impl Counter {
fn new(start: i32, end: i32) -> Self {
Counter { current: start, max: end }
}
}
impl Iterator for Counter {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.current <= self.max {
let value = self.current;
self.current += 1;
Some(value)
} else {
None
}
}
}
fn main() {
let counter = Counter::new(1, 5);
for num in counter {
println!("{}", num);
}
}
The Iterator pattern provides a way to access the elements of an aggregate object (like a list, array, or tree) sequentially without exposing its underlying representation. It defines a standardized interface for traversal, allowing algorithms to operate on collections without knowing their concrete type. This example implements an Iterator interface with a concrete ConcreteIterator for a simple ConcreteAggregate (a list of integers). The HasNext() and Next() methods fulfill the basic iteration protocol. This implementation fits idiomatic Go by utilizing interfaces to achieve abstraction and employing a receiver-based approach for method definition. The use of channels isn’t strictly necessary, but demonstrates a common concurrency pattern that can be incorporated into more complex iterators.
package main
import "fmt"
// Iterator defines the interface for accessing elements sequentially.
type Iterator interface {
HasNext() bool
Next() interface{}
}
// Aggregate defines the interface for objects that can be iterated over.
type Aggregate interface {
CreateIterator() Iterator
}
// ConcreteAggregate represents a collection of data.
type ConcreteAggregate struct {
data []int
}
func (ca *ConcreteAggregate) CreateIterator() Iterator {
return &ConcreteIterator{data: ca.data, index: 0}
}
// ConcreteIterator implements the Iterator interface for ConcreteAggregate.
type ConcreteIterator struct {
data []int
index int
}
// HasNext checks if there are more elements to iterate over.
func (ci *ConcreteIterator) HasNext() bool {
return ci.index < len(ci.data)
}
// Next returns the next element in the collection.
func (ci *ConcreteIterator) Next() interface{} {
if !ci.HasNext() {
return nil // Or handle end of iteration differently
}
item := ci.data[ci.index]
ci.index++
return item
}
func main() {
aggregate := &ConcreteAggregate{data: []int{1, 2, 3, 4, 5}}
iterator := aggregate.CreateIterator()
for iterator.HasNext() {
item := iterator.Next()
fmt.Println(item)
}
}
The Iterator pattern provides a way to access the elements of an aggregate object (like an array or list) sequentially without exposing its underlying representation. It defines a standard interface for creating iterators, allowing clients to traverse different aggregate structures using the same iteration protocol. This code implements a simple integer array iterator using structures and function pointers. It’s idiomatic C as it relies on explicit memory management, structs to bundle data/methods, and function pointers to achieve polymorphism without traditional object orientation. This approach keeps the code lightweight and performs well in C environments.
#include <stdio.h>
#include <stdlib.h>
// Define the Iterator interface
typedef struct {
int (*next)();
int (*hasNext)();
} Iterator;
// Define the Aggregate (Integer Array)
typedef struct {
int *data;
int size;
Iterator *iterator;
} IntegerArray;
// Create a new IntegerArray
IntegerArray* createIntegerArray(int *data, int size) {
IntegerArray *arr = (IntegerArray*)malloc(sizeof(IntegerArray));
if (arr == NULL) {
return NULL;
}
arr->data = data;
arr->size = size;
arr->iterator = (Iterator*)malloc(sizeof(Iterator));
if (arr->iterator == NULL) {
free(arr);
return NULL;
}
arr->iterator->next = integerArrayNext;
arr->iterator->hasNext = integerArrayHasNext;
return arr;
}
// Iterator specific functions
int integerArrayNext(IntegerArray *arr) {
static int index = 0;
if (index < arr->size) {
return arr->data[index++];
} else {
return -1; // Or any other appropriate out-of-bounds indicator.
}
}
int integerArrayHasNext(IntegerArray *arr) {
static int index = 0;
return index < arr->size;
}
// Example Usage
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
IntegerArray *array = createIntegerArray(numbers, size);
if (array == NULL) {
printf("Failed to create IntegerArray.\n");
return 1;
}
printf("Iterating over the array:\n");
while (array->iterator->hasNext(array)) {
printf("%d ", array->iterator->next(array));
}
printf("\n");
free(array->iterator);
free(array);
return 0;
}
The Iterator pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. It defines an interface for traversing a collection, allowing clients to request the next element and check if there are more elements, without knowing anything about how the collection is stored. This code showcases a ConcreteAggregate (a vector) and a ConcreteIterator to traverse it. The iterator provides hasNext() and next() methods. This C++ implementation leverages the Standard Template Library (STL) concepts and adheres to RAII principles by implicitly managing iterator lifecycle, characteristic of idiomatic C++ design.
#include <iostream>
#include <vector>
// Forward declaration
class Iterator;
// Aggregate interface
class Aggregate {
public:
virtual Iterator* createIterator() = 0;
};
// Iterator interface
class Iterator {
public:
virtual bool hasNext() = 0;
virtual int next() = 0;
};
// Concrete Aggregate
class ConcreteAggregate : public Aggregate {
private:
std::vector<int> data;
public:
void setData(const std::vector<int>& data) { this->data = data; }
Iterator* createIterator() override;
};
// Concrete Iterator
class ConcreteIterator : public Iterator {
private:
ConcreteAggregate* aggregate;
int current_index;
public:
ConcreteIterator(ConcreteAggregate* aggregate) : aggregate(aggregate), current_index(0) {}
bool hasNext() override {
return current_index < aggregate->data.size();
}
int next() override {
if (hasNext()) {
return aggregate->data[current_index++];
} else {
return -1; // Or throw an exception
}
}
};
Iterator* ConcreteAggregate::createIterator() {
return new ConcreteIterator(this);
}
int main() {
ConcreteAggregate concrete_aggregate;
std::vector<int> my_data = {1, 2, 3, 4, 5};
concrete_aggregate.setData(my_data);
Iterator* iterator = concrete_aggregate.createIterator();
while (iterator->hasNext()) {
std::cout << iterator->next() << " ";
}
std::cout << std::endl;
delete iterator; // Important for manual memory management
return 0;
}
The Iterator pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. It defines a standardized interface for traversing collections. This implementation uses C#’s IEnumerable and IEnumerator interfaces to create a custom iterator for a Playlist class. The GetEnumerator() method returns a new instance of the PlaylistIterator class, which handles moving through the playlist’s songs one by one. This leverages C#’s built-in iteration infrastructure (e.g., foreach loops) and avoids exposing the List<string> directly. It’s idiomatic C# as it fits seamlessly with the language’s collection handling mechanisms.
using System;
using System.Collections;
using System.Collections.Generic;
public class Playlist : IEnumerable<string>
{
private List<string> songs = new List<string> { "Song 1", "Song 2", "Song 3" };
public void AddSong(string song)
{
songs.Add(song);
}
public IEnumerator<string> GetEnumerator()
{
return new PlaylistIterator(songs);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class PlaylistIterator : IEnumerator<string>
{
private List<string> songs;
private int current = -1;
public PlaylistIterator(List<string> songs)
{
this.songs = songs;
}
public string Current
{
get
{
try
{
return songs[current];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
object IEnumerator.Current => Current;
public void Dispose() { }
public bool MoveNext()
{
current++;
return (current < songs.Count);
}
public void Reset()
{
current = -1;
}
}
public class Example
{
public static void Main(string[] args)
{
var playlist = new Playlist();
playlist.AddSong("Song 4");
foreach (string song in playlist)
{
Console.WriteLine(song);
}
}
}
The Iterator pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. It defines an interface for creating an iterator and uses that interface to traverse the collection.
This TypeScript example implements an iterator for a BookCollection. The BookCollection class doesn’t directly expose its books array. Instead, createIterator() returns a BookIterator instance, which holds a pointer to the current index. The iterator’s next() method returns the next book in the collection, and hasNext() indicates if there are more books to read. This fits TypeScript’s OOP style through the use of classes and interfaces and leverages type safety for Book objects.
interface Book {
title: string;
author: string;
}
interface Iterator<T> {
hasNext(): boolean;
next(): T | null;
}
class BookCollection {
private books: Book[];
constructor(books: Book[]) {
this.books = books;
}
createIterator(): Iterator<Book> {
return new BookIterator(this.books);
}
}
class BookIterator implements Iterator<Book> {
private books: Book[];
private index: number;
constructor(books: Book[]) {
this.books = books;
this.index = 0;
}
hasNext(): boolean {
return this.index < this.books.length;
}
next(): Book | null {
if (this.hasNext()) {
return this.books[this.index++];
}
return null;
}
}
// Example Usage:
const books = [
{ title: "The Cat in the Hat", author: "Dr. Seuss" },
{ title: "To Kill a Mockingbird", author: "Harper Lee" },
{ title: "1984", author: "George Orwell" },
];
const collection = new BookCollection(books);
const iterator = collection.createIterator();
while (iterator.hasNext()) {
const book = iterator.next();
console.log(`${book?.title} by ${book?.author}`);
}
The Iterator pattern provides a way to access the elements of an object sequentially without exposing its underlying representation. It defines a standard interface for traversing collections, allowing clients to request the next element without knowing how it’s implemented.
The code defines a BookCollection class that acts as a collection. It also defines a BookIterator class which implements the iterator interface (with next() and hasNext()). The BookCollection returns an instance of this iterator. This allows a client to iterate through the books without knowing the collection’s internal array structure. Using JavaScript’s class syntax and generator functions (in the iterator) is a modern and idiomatic approach to implementing this pattern. Generator functions simplify the creation of iterators by handling the state automatically with yield.
class Book {
constructor(title, author) {
this.title = title;
this.author = author;
}
toString() {
return `${this.title} by ${this.author}`;
}
}
class BookCollection {
constructor() {
this.books = [];
}
addBook(book) {
this.books.push(book);
}
createIterator() {
return new BookIterator(this.books);
}
}
class BookIterator {
constructor(books) {
this.books = books;
this.index = 0;
}
hasNext() {
return this.index < this.books.length;
}
next() {
if (this.hasNext()) {
return this.books[this.index++];
}
return null; // Or throw an error, depending on desired behavior
}
}
// Example usage:
const collection = new BookCollection();
collection.addBook(new Book("The Lord of the Rings", "J.R.R. Tolkien"));
collection.addBook(new Book("Pride and Prejudice", "Jane Austen"));
collection.addBook(new Book("1984", "George Orwell"));
const iterator = collection.createIterator();
while (iterator.hasNext()) {
const book = iterator.next();
console.log(book.toString());
}
The Iterator pattern provides a way to access a sequence of elements in an object without exposing its underlying representation. It defines an interface for creating iterators and allows clients to traverse the elements without knowing how they are stored. This is achieved by separating the traversal logic from the data structure. The Python implementation leverages the language’s built-in iterator protocol using __iter__ and __next__ methods. MyCollection provides __iter__ returning a MyIterator instance. MyIterator manages the current index for traversal and raises StopIteration when the end of the collection is reached, adhering to Python’s “duck typing” and enabling for loop compatibility.
class MyCollection:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return MyIterator(self.data)
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.data):
value = self.data[self.index]
self.index += 1
return value
else:
raise StopIteration
# Example usage
my_collection = MyCollection([1, 2, 3, 4, 5])
for item in my_collection:
print(item)
# Getting the next item manually.
my_iterator = iter(my_collection)
print(next(my_iterator))
print(next(my_iterator))
The Iterator pattern is a behavioral design pattern that provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It defines a standardized interface for traversing collections. This example demonstrates this with a BookCollection aggregate and a BookIterator that allows access to books one by one. Java’s built-in Iterator interface heavily relies on this pattern, and our implementation mirrors that. Using an explicit iterator class promotes loose coupling between the collection and the client code, increasing flexibility. This approach utilizes a private inner class for the iterator, encapsulating its logic within the collection.
import java.util.NoSuchElementException;
class Book {
private String title;
public Book(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
class BookCollection {
private Book[] books;
private int bookCount;
public BookCollection(Book[] books) {
this.books = books;
this.bookCount = books.length;
}
public BookIterator createIterator() {
return new BookIterator();
}
private class BookIterator implements java.util.Iterator<Book> {
private int position = 0;
@Override
public boolean hasNext() {
return position < bookCount;
}
@Override
public Book next() {
if (!hasNext()) {
throw new NoSuchElementException("No more books in the collection.");
}
return books[position++];
}
}
}
public class IteratorExample {
public static void main(String[] args) {
Book[] bookList = {new Book("The Hitchhiker's Guide to the Galaxy"), new Book("Pride and Prejudice"), new Book("1984")};
BookCollection collection = new BookCollection(bookList);
BookIterator iterator = collection.createIterator();
while (iterator.hasNext()) {
Book book = iterator.next();
System.out.println(book.getTitle());
}
}
}