Writing iterators for custom collections in Java is quite complicated, because instead of writing straight-forward code that provides one element after the other, you essentially have to write a state machine:
public class CustomCollection<T> implements Iterable<T>
{
private T[] data;
private int size;
@Override
public Iterator<T> iterator()
{
return new Iterator<T>()
{
private int cursor = 0;
@Override
public boolean hasNext()
{
return cursor < size;
}
@Override
public T next()
{
return data[cursor++];
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
// ...
}
For collections more complicated than an array list or a linked list, getting these state machines correctly is a daunting task. In fact, the C# design team deemed writing custom iterators complicated enough to introduce special language support (yield return) for letting the compiler build the state machines.
Is something like yield return coming in the next version of Java? Or are there any library solutions that make my life easier when it comes to writing my own iterators in Java?
Java has always provided a mechanism for maintaining state and continuing execution at a later point in time: threads. The basic idea for my library solution is to let a
ConcurrentIterableproduce the elements in one thread, and let aConcurrentIteratorconsume them in another, communicating via a bounded queue. (This is generally known as the producer/consumer pattern.)First, here is a demonstration of the simplified usage:
Note the complete absence of state machines. All you have to do is derive from
ConcurrentIterableand implement the methodprovideElements. Inside this method, you write straight-forward code which callsprovideElementfor each element in the collection.Sometimes a client does not iterate through the entire collection, for example in a linear search. You can stop providing elements as soon as an abortion is detected by checking
iterationAborted():It is perfectly fine not to check
iterationAborted(), as long as you do not care about the additional elements being generated. With infinite sequences, checkingiterationAborted()is mandatory.How can the producer detect that the consumer has stopped iterating? This is implemented by having a strong reference to a token in the consumer and a weak reference to that same token in the producer. When the consumer stops iterating, the token becomes eligible for garbage collection, and it will eventually become invisible to the producer. From then on, all new elements will simply be discarded.
(Without this precaution, under certain circumstances the bounded queue could eventually fill up, the producer would enter an infinite loop, and the contained elements would never be garbage collected.)
And now for the implementation details:
ConcurrentIterable.java
CommunicationChannel.java
ConcurrentIterator.java
Option.java
Let me know what you think!