Accessing the elements of one of the collection classes from the java.util package is usually easy. To get a single element you can use the get(int) method of the List interface or you can get all elements via the interator() method and afterwards iterate over them.
But how many times do you have to do additional checks on the elements of an iteration before you can process them? Some elements of an iteration must be skipped and other ones must be transformed into another object type or you don't want to process the element itself but a object returned by one of its getter methods. The following wrapper classes are specialized implementations of the java.util.Iterator interface written to support you with such things and to avoid code duplication.
Since all this iterators are implemented as proxies for other iterator instances it is important to point our some things which affect all iterator implementations and are important for understanding them and to solve possible problems.
One common situation is that you have to process only those elements of an iteration which fulfill a certain condition.
Lets take a look at the following example, there you have to implements a method getAllFooBarCustomers() which should return all customers of the current group for those some criterions are true. Most of us would solve this in the following way:
public class CustomerGroupImpl implements CustomerGroup { public Iterator getAllFooBarCustomers() { Collection res = new LinkedList(); Iterator itr = allCustomers.iterator(); while (itr.hasNext()) { Customer c = (Customer)itr.next(); if (c.isActive() && c.getValue() > 23 && ...) res.add(c); } return res.iterator(); } ... }
Using the FilterIterator() you could simplify this method a little bit and avoid the overhead on an additional collection. What does it do? The FilterIterator filters the elements of a given iteration according to a given condition, which is expressed as a UnaryPredicate and applies this predicate to each element of the iteration. If the predicate is true for the element it is returned. Otherwise the element is skipped. Refactoring the code above is quite easy done. The only thing you have to do is to extract the conditional logic into a UnaryPredicate and to wrap the iteration of the original collection with an instance of FilterIterator as shown in the following example.
public class CustomerGroupImpl implements CustomerGroup { public Iterator getAllFooBarCustomers() { return new FilterIterator(new UnaryPredicate() { public boolean exec(Object o) { Customer c = (Customer)o; return c.isActive() && c.getValue() > 23 && ...; } }, allCustomers.iterator()); } }
Additional you can transform the anonymous inner class to a normal one or a constant.
final public CustomerPredicates { final static UnaryPredicate FOOBAR_CUSTOMER = new UnaryPredicate() { ... }; }
Using this approach you can even implement an very dynamic check for every element by implementing an UnaryPredicte which is a adaptor for another method or class.
As the name of this iterator indicates, it allows you to turn around the order of the elements in a given iterator.
... Iterator i = new ReverseIterator(members.iterator()); while (i.hasNext()) { System.out.println(i.next()); } ...
Indeed, the implementation of this iterator is quite simple. It puts all the elements of the source iterator in an internal cache and returns them it the reverse order. Since this iterator works not on the original iteration, the remove() method is not support.
This iterator is a specialized implementation of the ReverseIterator for ListIterator instances. As described above, the ReverseIterator doesn't support the remove() method and needs additional memory to cache the elements of the source iteration. The ListIterator offers already the possiblity to return its elements in the reverse order. The only thing you have to do is not to call hasNext() but hasPrevious() and instead of next() the previous() method. For what do you need this iterator, if this functionality is already given? Before I will explain why, lets have a look at the small interface below:
public interface MemberHome { Iterator getAllByNameStartingWithLowest(); Iterator getAllByNameStartingWithHighest(); }
Let us assume, that you have to implement this interface and you store all your Member objects in a sorted list, which implements the List interface and offers for this reason the listIterator() method which returns a ListIterator instance. Supposably the implementation of the getAllByNameStartingWithLowest would be the following:
... publiv Iterator getAllByNameStartingWithLowest() { return memberlist.iterator(); }
The implementation of the second method is a little bit more complicated. The interface specifies what the implementation has to return a Iterator and nothing else. But we have a List and this list has already the possiblity to return the its elements in reverse order. If you have access to the interface, you could change the return value of this method to ListIterator. But this means, that you adapt the interface to your implementation and that the caller has access to all other methods of the ListIterator. Wrapping the ListIterator with a ListReverseIterator will solve this problem, since it translates every hasNext() call to hasPrevious() and every call to next() to previous(). The remove() method is full supported.
Using the ListReverseIterator the implementation of getAllByNameStartingWithHighest() could be the following:
... public Iterator getAllByNameStartingWithHighest() { return new ListReverseIterator(memberlist.listIterator()); } ...
An instance of this class doesn't return the elements of the underlying iteration directly but the result of a custom UnaryFunction to which each element is handed over. This approach allows you to apply some changes to every object before returning it or to return a completely different object.
public Iterator getEnvelope() { Collection res = new LinkedList(); Iterator i = members.iterator(); while (i.hasNext()) { Envelope e = new Envelope((Member)i.next()); e.setBCC("sysadmin@nowhere.tcf"); ... } return res.iterator(); }
To integrate the TransformatorIterator in the code above you have to extract the code in the while loop into a UnaryFunction and to wrap the original iteration with an instance of this iterator together with the UnaryPredicate.
The result of this changes could be the following code.
public Iterator getEnvelope() { return new TransformatorIterator(members.iterator(), new UnaryFunction() { public void exec(Object o) { Envelope e = new Envelope((Member)i.next()); ... return e; } }); }
This iterator can help you also, if you want to give the caller only access to cloned copies of your objects.
For many methods null is a valid parameter and for other ones it isn't. This can cause problems if you have to fed a method which doesn't permit null with the elements of an iteration which could contain null elements.
If you don't want to check every element by our own, you can wrap a given iteration with the SkipNullIterator which skips all null elements of an iteration and returns only the other ones.
... public void killAllMausSchubsAdmins(Collection input) { Iterator i = new SkipNullIteratior(input); while (i.hasNext()) { // Now you are on the save side. i.next() will never // return null! ... } } ...
Above we introduced the FilterIterator which helps you to skip elements in an iteration. The opposite does the GlueingIterator for you. With it you can join two or more Iterator instances together so, that the caller can use them as a single one.
This iterator can be instanciated with two different types of constructors. The first one takes two iterator instances.
public Iterator getAllActiveDogs() { ... } public Iterator getAllInactiveDogs() { ... } public Iterator getAllDogs() { return new GlueingIterator(getAllActiveDogs(), getAllInactiveDogs()}; }
The other one takes an iterator array of an arbitrary number of iterators and joins them.
public Iterator getAllActiveDogs() { ... } public Iterator getAllInactiveDogs() { ... } public Iterator getAllDogs() { return new GlueingIterator(new Interator[] { getAllActiveDogs(), getAllInactiveDogs() }); }
To an already created instance you can add more iterators via addIterator(Iterator) if it hasn't been used. That means neither hasNext() nor next() have been called by some one. Otherwiese you will get an IllegalStateException.
If it is not clear in which state the iterator is, then the isRunning() method should be called to check the current state of the iterator. If this it returns true the iterator is in use and addIterator(...) shouldn't be called.
Independent of the creation, the GlueingIterator keeps the order of the joined iterations, i.e. that all elements are returned in the same order as if the caller would iterate over every single iterator one after another.
This iterator is only an adaptor for the java.util.Enumeration to use it as an Iterator. Since the Enumeration provides only methods to iterate over its elements, the remove() method isn't supported.
public Iterator getFooBar() { Iterator i = new EnumeratrionIterator(obj.getEnumeration()); return i; }