- External Iterators
- Internal Iterators
- Internal Iterators versus External Iterators
- The Inimitable Enumerable
- Using and Abusing the Iterator Pattern
- Iterators in the Wild
- Wrapping Up
Internal Iterators versus External Iterators
While either an internal iterator or an external iterator will do the basic job of stepping through an aggregate, there are some practical differences to consider. External iterators certainly have some advantages. For example, when you use an external iterator, the client drives the iteration. With an external iterator, you won’t call next until you are good and ready for the next element. With an internal iterator, by contrast, the aggregate relentlessly pushes the code block to accept item after item.
Most of the time, this difference does not matter. But what if you are trying to merge the contents of two sorted arrays into a single array that was itself sorted? This kind of merge is actually fairly easy with an external iterator like ArrayInterator: We simply create an iterator for the two input arrays and then merge the arrays by repeatedly pushing the smallest value from either of the iterators onto the output array.
def merge(array1, array2) merged = [] iterator1 = ArrayIterator.new(array1) iterator2 = ArrayIterator.new(array2) while( iterator1.has_next? and iterator2.has_next? ) if iterator1.item < iterator2.item merged << iterator1.next_item else merged << iterator2.next_item end end # Pick up the leftovers from array1 while( iterator1.has_next?) merged << iterator1.next_item end # Pick up the leftovers from array2 while( iterator2.has_next?) merged << iterator2.next_item end merged end
I am not sure how you would implement a merge like this using internal iterators.
A second advantage of external iterators is that, because they are external, you can share them—you can pass them around to other methods and objects. Of course, this is a bit of a double-edged sword: You get the flexibility but you also have to know what you are doing. In particular, beware of multiple threads getting hold of a non-thread-safe external iterator.
The main thing that internal iterators have going for them is simplicity and code clarity. External iterators have that extra moving part, the iterator object. In our array example, we not only have the array and the client code, but also the separate ArrayInterator object. With internal iterators, there is no separate iterator object to manage (“Did I call next yet?”), just a stretch of more or less in-line code.