Diagram of the Iterator pattern
The Iterator pattern is used in situations where a collection of items needs to be iterated over. That is, where each item needs to be accessed in some sequence, one at a time.
The C++ Standard Template Library (STL) provides many examples of such iterators on the various STL container classes. And in general, a C++- based program should use those containers whenever possible because they are well-tested and can have any number of pre-defined algorithms applied, including the ability to easily iterate over the contents of the containers.
Other programming languages have varying degrees of built-in support for iterators over containers. For example, in C# or Python, all containers can provide an iterator over the contents of the container. Again, in general, you should use the built-in containers provided by the language whenever possible to take advantage of well-tested code, including the iterators.
A non-object-oriented kind of iterator uses an opaque token to represent the index into the data. A function retrieves this token pointing to the first item. A second function increments the index in the token to point to the next item. These functions can be implemented on the container itself as methods or they can be functions that accept the container and know how to get the number of items from the container and how to get the items themselves from the container.
An iterator doesn't actually have to have a container to draw from or at least not a container visible to the caller. The iterator could be three functions: One to start the iteration, the second to retrieve the next item in the iteration, and the third function to release or close the iteration. The functions know what they are iterating over and the starting function takes parameters to adjust what is actually iterated over. A good example of this kind of iterator is the Win32 find file functions, FindFirstFile(), FindNextFile(), FindClose().
The simplest kind of iterator is typically implemented on the container itself as a pair of methods: One method returns the number of items to iterate over and a second method returns the item at the specified index. The caller then increments (or decrements) the index passed into the second function. This is a form of random-access iterator where the items can be accessed in any order.
How to Use
[C#, C++, Python] Having said all that, here is an example of how an object-oriented iterator can be implemented. In this case, the example uses the interface, IIterator
, to represent an iterator to the caller. A custom container class, IteratorContainer_Class
, provides an instance of this interface to allow the caller to iterate forward over the container. This custom container can expose multiple iterators, each independent of the others.
[C] In C, there are no objects, so functions are used to get an iterator over the various kinds of data. Another function is used to move the iterator over the internal data to get the next element. Note how each type of iterator has its own dedicated pair of functions, such as Iterator_GetItems(), and Iterator_NextItem().
The use of multiple iterators on the same container is normally used only when the container is a read-only type; that is, the contents will not be changing. Once the contents are changed (added or removed), all iterators are considered invalid.
C++
void Iterator_Exercise()
{
std::cout << std::endl;
std::cout << "Iterator Exercise" << std::endl;
IteratorContainer_Class items;
std::cout << " Iterating over keys only:" << std::endl;
auto keyIterator = items.GetKeys();
std::string item;
while (keyIterator->Next(item))
{
}
std::cout << " Iterating over values only:" << std::endl;
auto valueIterator = items.GetValues();
std::string value;
while (valueIterator->Next(value))
{
}
std::cout << " Iterating over all items:" << std::endl;
auto itemIterator = items.GetItems();
while (itemIterator->Next(key_value_pair))
{
key_value_pair.Key.c_str(), key_value_pair.Value.c_str())
<< std::endl;
}
std::cout << " Done." << std::endl;
}
std::string formatstring(const char *fmt,...)
Use the given string and arguments to return a buffer containing the formatted string....
Represents a key/value pair where the key and value are strings.
C#
public void Run()
{
Console.WriteLine();
Console.WriteLine("Iterator Exercise");
IteratorContainer_Class items = new IteratorContainer_Class();
Console.WriteLine(" Iterating over keys only:");
var keyIterator = items.GetKeys();
for (string? item = keyIterator.Next(); item != null; item = keyIterator.Next())
{
Console.WriteLine(" {0}", item);
}
Console.WriteLine(" Iterating over values only:");
var valueIterator = items.GetValues();
for (string? item = valueIterator.Next(); item != null; item = valueIterator.Next())
{
Console.WriteLine(" {0}", item);
}
Console.WriteLine(" Iterating over all items:");
var itemIterator = items.GetItems();
for (
ItemPair? item = itemIterator.Next(); item !=
null; item = itemIterator.Next())
{
Console.WriteLine(" {0} = {1}", item.Key, item.Value);
}
Console.WriteLine(" Done.");
}
Python
def Iterator_Exercise():
print()
print("Iterator Exercise")
items = IteratorContainer_Class()
print(" Iterating over keys only:")
keyIterator = items.GetKeys()
item = keyIterator.Next()
while item:
print(" {0}".format(item))
item = keyIterator.Next()
print(" Iterating over values only:")
valueIterator = items.GetValues()
value = valueIterator.Next()
while value:
print(" {0}".format(value))
value = valueIterator.Next()
print(" Iterating over all items:")
itemIterator = items.GetItems()
key_value_pair = itemIterator.Next()
while key_value_pair:
print(" {0} = {1}".format(key_value_pair.Key, key_value_pair.Value))
key_value_pair = itemIterator.Next()
print(" Done.")
C
void Iterator_Exercise(void)
{
printf("\nIterator Exercise\n");
printf(" Iterating over keys only:\n");
{
printf(
" %s\n", keyIterator.
key);
}
printf(" Iterating over values only:\n");
{
printf(
" %s\n", valueIterator.
value);
}
printf(" Iterating over all items:\n");
{
}
printf(" Done.\n");
}
void Iterator_GetItems(ItemIterator *iterator)
Retrieve an iterator over the whole items in the internal data structure.
void Iterator_GetValues(ValueIterator *iterator)
Retrieve an iterator over the values in the internal data structure.
void Iterator_GetKeys(KeyIterator *iterator)
Retrieve an iterator over the keys in the internal data structure.
bool Iterator_NextValue(ValueIterator *iterator)
Retrieve the next value (const char*) from the iterator.
bool Iterator_NextKey(KeyIterator *iterator)
Retrieve the next key (const char*) from the iterator.
bool Iterator_NextItem(ItemIterator *iterator)
Retrieve the next whole item (ItemPair) from the iterator.
Represents an iterator that retrieves whole items.
ItemPair item
The key/value pair pointed to by this iterator. item.key and item.value are NULL if Iterator_NextItem...
const char * value
The value part of the item.
const char * key
The key part of the item.
Represents an iterator that retrieves the key of each item.
const char * key
The key pointed to by this iterator. key is NULL if Iterator_NextKey() has not yet been called or the...
Represents an iterator that retrieves the value of each item.
const char * value
The value pointed to by this iterator. value is NULL if Iterator_NextValue() has not yet been called ...
RUST
(Apologies. Doxygen does not understand Rust syntax and therefore cannot colorize the code.)
pub fn iterator_exercise() -> Result<(), String> {
println!("");
println!("Iterator Exercise");
// For this example, the class already has built into it the data
// to be iterated over.
// Instantiate the container to be iterated over.
let items = Items::new();
println!(" Iterating over keys only:");
let mut key_iterator = items.get_keys();
loop {
match key_iterator.next() {
Some(key) => println!(" {key}"),
None => break,
}
}
println!(" Iterating over values only:");
let mut value_iterator = items.get_values();
loop {
match value_iterator.next() {
Some(value) => println!(" {value}"),
None => break,
}
}
println!(" Iterating over all items:");
let mut item_iterator = items.get_items();
loop {
match item_iterator.next() {
Some(item) => println!(" {} = {}", item.key, item.value),
None => break,
}
}
println!(" Done.");
Ok(())
}
See Also