First of all, apologies for introducing a new word.

But it seemed like a good idea to draw attention to the similarity between iteration and stream processing constructs.

So, "striteration" is a term to indicate a combination of both Streams and Iteration.

The Striterator object implements the Iterator and the Enumeration interfaces along with the additional methods that add support for stream type processing functionality.

Probably the best way to explain Striteration is to give a number of examples.

Creation

A Striterator must initially be given an Iterator object. For example :

Striterator striter = new Striterator(list.iterator());

The resulting Striterator can be used anywhere an Iterator or Enumeration is required.

Appending

Want to "append" two iterators?

striter.addFilter(new Appender(otherList.iterator()));

In fact, this is such a common requirement, that a utility is provided on the Striterator object.

striter.append(otherList.iterator());

Checking

Need to "filter out" certain objects?

striter.addFilter(new Filter() {
  protected boolean isValid(Object obj) {
    return obj instanceof SomeClass;
  }
} );

Would provide a type check on each object.

Resolving

Often, the object of interest is not the object directly in the iteration:

public Iterator getNames(Iterator members) {
  Striterator names = new Striterator(members);
  return names.addFilter(new Resolver() {
    protected Object resolve(Object obj) {
      return ((NamedType) obj).getName();
    }
  } );
}

With this example one of the features of Striteration is apparent, that you are able to create new iterators to be processed elsewhere.

It can be used to provide set abstractions in a similar way to SQL type database queries.

Expanding

Along with resolving the requirement to expand sets is also common. Consider some getGrandchildren method :

public Iterator getGrandChildren() {
  Striterator gchildren = new Striterator(getChildren());
  
  return gchildren.addFilter(new Expander() {
    protected Iterator expand(Object obj) {
      return ((Human) obj).getChildren();
    }
  } );
}

You may have realised that the addFilter must be returning an Iterator. In fact it returns a Striterator so that filters can be chained together.

Let's use Striterator declarations and see how much neater things get:

public Striterator getNames(Striterator members) {
  return members.addFilter(new Resolver() {
    protected Object resolve(Object obj) {
      return ((NamedType) obj).getName();
    }
  } );
}

public Striterator getChildren() {
  return new Striterator(m_children.iterator());
}

public Striterator getGrandChildren() {
  return getChildren().addFilter(new Expander() {
    protected Iterator expand(Object obj) {
      return ((Human) obj).getChildren();
    }
  } );
}

public void printGrandchildrenNames() {
  Striterator gcnames = getNames(getGrandChildren());
	
  while (gcnames.hasNext()) {
    System.out.println(gcnames.next());
  }
}

Other Striteration Patterns

There are a number of other patterns already provided for :

The Striterator protocol is designed so that it is easy to add new patterns.

Utility Classes

A few other classes are provided to support Striterator constructs.

ProxyIterator

Takes an Enumeration object to provide an Iterator

EmptyIterator

Just what it says - a final class implementing Iterator but is always empty. Very handy!

SingleValueIterator

Just what it says - a final class implementing Iterator that is initialised with the single object it will return. Also very useful.