Chapter 19. The Iterator Pattern

The Iterator pattern is one of the simplest and most frequently used of the design patterns. It allows you to move through a list or collection of data using a standard interface without having to know the details of that data's internal representations. You can also define special iterators that perform some special processing and return only specified elements of the data collection.

Motivation

The Iterator pattern is useful because it provides a defined way to move through a set of data elements without exposing what is taking place inside the class. It is an interface; thus you can implement it in any way that is convenient for the data that you are returning. Design Patterns suggests that a suitable interface for an iterator might be the following:

public interface Iterator {
    public Object     First() ;
    public Object     Next();
    public boolean    isDone();
    public Object     CurrentItem();
}

Using this interface, you can move to the top of the list, move through the list, find out if there are more elements, and find the current list item. It is easy to implement and has certain advantages. However, the Iterator of choice in Java is the following:

public interface Enumeration {
    public boolean    hasMoreElements();
    public Object     nextElement();
}

Not having a method to move to the top of a list might seem restrictive at first. However, it is not a serious problem in Java because you customarily obtain a new instance of the Enumeration each time that you want to move through alist. Disadvantages of the Java Enumeration over similar constructs in C++ and Smalltalk are the strong typing of the Java language, as well as its lack of templates. This prevents the hasMoreElements method from returning an object of the actual type of the data in the collection without an annoying requirement to cast the returned Object type to the actual type. Thus, while the Iterator orEnumeration interface is intended to be polymorphic, this is not directly possible in Java.

Enumerations in Java

The Enumeration type is built into the Vector and Hashtable classes. Rather than these classes implementing the two methods of the Enumeration directly,both contain an elements method that returns an Enumeration of that class'sdata.

public Enumeration elements();

This elements method is really a kind of Factory method that produces instances of an Enumeration class.

Then you move through the list using the following simple code:

Enumeration e = vector.elements();
while (e.hasMoreElements()) {
    String name = (String)e.nextElement();
    System.out.println(name);
}

In addition, the Hashtable also has the keys method, which returns an Enumeration of the keys to each element in the table:

public Enumeration keys();

This is the preferred style for implementing Enumerations in Java and has the advantage that you can have any number of simultaneous active enumerations of the same data.

Sample Code

Let's reuse the list of swimmers, clubs, and times we described for the Interpreter in Chapter 18 and add some enumeration capabilities to the KidData class. This class is essentially a collection of Kid objects, each with a name, club, and time stored in a Vector.

public class kidData {
    private Vector kids;
    private Hashtable clubs;
//--------------
    public kidData(String filename) {
        kids = new Vector();
        clubs = new Hashtable();
        InputFile f = new InputFile(filename);
        String s = f.readLine();
        while (s != null) {
            if (s.trim().length() > 0) {
                Kid k = new Kid(s);
                kids.addElement(k);
                clubs.put (k.getClub (), k.getClub ());
            }
            s = f.readLine();
        }
    }

To obtain an Enumeration of all of the Kids in the collection, we just return the Enumeration of the Vector itself.

public Enumeration elements() {
return kids.elements();
    }

Filtered Iterators

Having a clearly defined method of moving through a collection is helpful. However, you can also define filtered Enumerations that perform some computation on the data before returning it. For example, you could return the data ordered in some particular way or return only those objects that match a particular criterion. Then, rather than have a lot of very similar interfaces for these filtered Enumerations, you provide a method that returns each type of Enumeration, with each one having the same methods.

The Filtered Enumeration

Suppose that we want to enumerate only those kids who belong to a certain club. We need a special Enumeration class that has access to the data in the KidData class. This is very simple to achieve because the elements method just defined gives us that access. So we need to write only an Enumeration that returns kids belonging to a specified club.

public class kidClub
implements Enumeration {
    private String clubMask;      //name of club
    private Kid kid;              //next kid to return
    private Enumeration ke;       //gets all kids
    private kidData kdata;        //class containing kids
//--------------
    public kidClub(KidData kd, String club) {
        clubMask = club;          //save the club
        kdata = kd;               //copy the class
        kid = null;               //default
        ke = kdata.elements();    //get the Enumerator
    }
//--------------
    public boolean hasMoreElements() {
        //return true if there are any more kids
        //belonging to the specified club
        boolean found = false;
        while (ke.hasMoreElements() && ! found) {
            kid = (Kid)ke.nextElement();
            found = kid.getClub().equals(clubMask);
        }
        if (! found)
            kid = null;    //set to null if none are left
        return found;
    }
//--------------
    public Object nextElement() {
        if (kid != null)
            return kid;
        else
            //throw an exception if access is past the end
                throw new NoSuchElementException();
    }
}

All of the work is done in the hasMoreElements() method. This method scans through the collection for another kid belonging to the club specified in the constructor and either saves that kid in the kid variable or sets it to null and then returns either true or false. The nextElement() method either returns that next kid variable or throws an exception if there are no more kids. Note that under normal circumstances, this exception is never thrown, since the has More Elements boolean should have already told you not to ask for another element.

Finally, we need to add a method, kidsInClub, to KidData to return this new filtered Enumeration.

public Enumeration kidsInClub(String club) {
        return new kidClub(this, club);
   }

This method passes the instance of KidClub to the Enumeration class KidClub along with the club's initials. Figure 19.1 shows a simple program. The program displays all of the kids on the left side, fills a combo box with a list of the clubs, and then allows the user to select a club. Finally, it fills the right-hand list box with the names of those kids who belong to a single club.

A simple program illustrating filtered Enumeration.

Figure 19.1. A simple program illustrating filtered Enumeration.

The class diagram is shown in Figure 19.2.

The classes used in the filtered Enumeration.

Figure 19.2. The classes used in the filtered Enumeration.

Note that the elements method in KidData supplies an Enumeration and the kidClub class is, in fact, itself an Enumeration class.

Consequences of the Iterator Pattern

Use of the Iterator pattern has the following consequences:

  1. Data modification . The most significant question resulting from the use ofiterators concerns iterating through data while it is being changed. If your code is wide ranging and only occasionally moves to the next element, an element might be added or deleted from the underlying collection whileyou are moving through it. It is also possible that another thread could change the collection. There are no simple solutions to this problem. You can make an Enumeration thread safe by declaring the loop to be synchronized, but if you want to move through a loop using an Enumeration and delete certain items, you must be careful of the consequences. Deleting or adding an element might mean that a particular element isskipped or accessed twice, depending on the storage mechanism that you are using.

  2. Privileged access . Enumeration classes might need to have some sort of privileged access to the underlying data structures of the original container class so that they can move through the data. If the data are stored in a Vector or Hashtable, accomplishing this is fairly easy. But if it is some other collection structure contained in a class, you will probably have to make that structure available through a get operation. Alternatively, you could make the Iterator a derived class of the containment class and access the data directly. The friend class solution available in C++ does not apply in Java. However, classes defined in the same module as the containing class do have access to the containing class's variables.

  3. External versus internal Iterators .  Design Patterns describes two types of Iterators: external and internal. Thus far, we have described only external iterators. Internal Iterators are methods that move through the entire collection, performing some operation on each element directly, without any specific requests from the user. Internal Iterators are less common inJava, but methods that normalize a collection of data values to lie between 0 and 1 or that convert all of the strings to a particular case are possible. In general, external Iterators give you more control because the calling program accesses each element directly and can decide whether to perform an operation on it.

Composites and Iterators

Iterators, or in the current case Enumerations, are also an excellent way to movethrough Composite structures. In the Composite of an employee hierarchy developed in Chapter 18, each Employee contains a Vector whose elements method allows you to continue to enumerate down that chain. If that Employee has no subordinates, the hasMoreElements method correctly returns false.

Iterators in Java 1.2

Java 1.2 introduces the following Iterator interface that is quite similar to the Enumeration interface:

public interface Iterator {
    public boolean hasNext();
    public Object next();
    public void remove();
}

The Iterator and the Enumeration differ only in the Iterator's simpler names and remove method. The remove method removes the last object returned from the next method and can be called only once per next method.

In Java 1.2, the Vector class now has an iterator method that returns an instance of the Iterator class. The Hashtable class, however, has not been updated—rather, it has been supplanted by a series of Collection and Map classes. The HashMap class is a hash table implementation based on a Map interface that has an elements method. This elements method returns an instance of the Collection class, which in turn returns an Iterator. Thus, to iterate through a HashMap, you obtain an iterator as follows:

private HashMap clubs;
//.. fill hash map
Iterator enum = clubs.values().iterator();

Thought Question

  1. The ListIterator interface in Java 1.2 applies to objects of type List. It allows you to move through a list in either direction and modify elements of the list. Rewrite the J2Iterator to allow and use these additional features.

Programs on the CD-ROM

Program Description

IteratorIterDemo.java

Demonstrates the filtered iterator. Allows the selection of a single club from a combo box and then displays those members of that club in the right-hand list box.

IteratorJ2IteratorJ2IterDemo.java

Same program using the Iterator interface and the HashMap to store the list of clubs.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset