Design Patterns in Go: Using Iterator to traverse our containers

Introduction

In Design Patterns, the Iterator is a way of traversing over a container, that is access each of the container’s elements. It is used as a convenient way to traverse over lists or arrays, but it could also be used to traverse over binary trees for example.

What does it look like. It looks like this:

Iterator UML Scheme

A bit of explanation can be handy:

  1. An iterator basically has two methods: next() and hasNext()
  2. The next() method returns the next element in the container
  3. The hasNext() method returns a bool, true if a next element is available, false if there is no next element

Implementation in Go

To demonstrate this pattern I will build an iterator that will just yield even numbers up to a certain user-defined limit.

In an empty directory type:

go mod init github.com/go-iterator-pattern

Now open your favourite IDE and add a ‘main.go’ file. We will start with the preliminaries:

package main

Next we define the EvenNumberIterator:

type EvenNumberIterator struct {
	current int32
	limit   int32
}

A short explanation:

  1. current is used to keep track of which number we are delivering
  2. limit is user-defined and makes sure we only yield a certain number of elements

Next we define the constructor for this struct:

func NewEvenNumberIterator(limit int32) *EvenNumberIterator {
	return &EvenNumberIterator{current: 0, limit: limit}
}

Some points:

  • We return a reference to the iterator. Considering that the size of this struct is small, this might seem superfluous, however, with bigger structures this can be a time- and memorysaver.
  • The iterator is initialized with the limit set by the user, and current set to 0, as we will start from zero for this example.

Now we will define the two basic iterator methods:

func (i *EvenNumberIterator) Next() int32 {
	i.current++
	if i.current%2 >= i.limit {
		return 0
	}
	return i.current * 2
}

func (i *EvenNumberIterator) HasNext() bool {
	return i.current < i.limit
}

Again, a short breakdown:

  1. The Next() method returns the next number if there is one. It checks if the current value is not over the limit, and since we only yield even numbers, we check if current%2 i.e. current modulo 2 is not over the limit
  2. The HasNext() method returns true if we have more numbers, false if there are none.

Time to test

To test this, we will add a main function:

func main() {
	iterator := NewEvenNumberIterator(10)
	for iterator.HasNext() {
		println(iterator.Next())
	}
}

All this does is:

  1. Instantiate the iterator
  2. Loop over it using the for statement, retrieving the next element using the Next() method, and making sure we are in the bounds of our limit with the HasNext() method

Conclusion

As you can see, as with many things in Go, it is very easy to build a simple iterator. My advice however would be that whenever you can, use Go’s built-in iterators.

One possible extension of this iterator would be to make it multi-threaded, but that will be the subject of another post.

Leave a Reply

Your email address will not be published. Required fields are marked *