Flexible Iterators
Java has some odd quirks which make it far more inflexible than it needs to be. For example, many programs have data structures which need to be iterated both forwards and backwards, and some algorithms require treating the first or last element differently than the others. My goal here is to find a tweak to the Java language that would permit use of the foreach loop in all of these cases. To achieve that, it would be nice if the data structure in question could return different iterators appropriate to the task at hand.
Let’s first review the laborious, multi-step process of gifting a class to support the foreach syntactic sugar.
- Declare the class with so that it
implements Iterable<Data>
. - Define a method
Iterator<Data> iterator()
. - Implement an appropriate
class DataIterator extends Iterator<Data>
- Define methods supported by all
Iterator
s: next(), hasNext(), and remove().
The foreach loop, which looks like:
for(Data d : collection) { // do something with d } |
desugars into
Iterator it = collection.iterator(); while (it.hasNext()) { Data d = it.next(); // do something with d } |
Knowing this implementation, I can achieve my stated desire in one of two ways 1. a syntax change or 2. a language change. I shall first present the syntax change because it’s such a horrible idea.
Syntax Modification
We first observe that the foreach loop desugars into a call to iterator()
passing no arguments. This constraint forces each class into a box where it can only implement one kind of iteration. Immediately, my object oriented (damaged) brain thought to alleviate this constraint by overloading of the iterator()
method with versions accepting different arguments. For example, a DataIterator which allowed slicing might be called with three arguments: start
, end
, and stride
. The foreach loop syntax can be extended to perform this lookup, by a small tweak to the desugaring:
for(Data d : collection)(start, end, stride) { // do something with d } |
The syntactic change can even be made backwards compatible with the use of varargs.
Semantic Modification
Change the semantics of the foreach desugarer so that it can handle both Iteratable
s and Iterator
s.
This is by far the easiest change, because I find it comparatively easy to gift my class with many well-named methods which each return an Iterator
.
The same example reads much better now:
for (Data d : collection.slice(start, end, stride)) { // do something with d } |
Instead of slice
I could also implement reverse
or any other kind of iteration.
I wondered why the Java implementors did not already put in the ability to handle both Iteratable
s and Iterator
s.
An answer on StackOverflow, of course, held the standard objection:
The reason for-each loops require an iterable is to allow the same object to be traversed multiple times (so that you can use multiple for-each loops over the same object without surprising behaviour), whereas an iterator only allows one traversal. If for-each allowed iterators to be used, the behaviour would be surprising to programmers who didn’t realise that their iterators would be exhausted after the loop is run.
Closing Remarks
My postdoc Per pointed out that Python programmers obviously have an easier time. Because Python has dynamic lookup mechanisms that allow the for-in loop to accept any iterator, it supports much more composition, allowing filters, generators, and iterators.