CS 136 - Lecture 20

  1. Enumerations and Iterators
    1. Examples of iterators
  2. Ordered Structures

Enumerations and Iterators

We often need a way of cycling through all of the elements of a data structure. There exists a built-in interface in java.util:
public interface Enumeration {
    public boolean hasMoreElements();
    // pre: Associated structure, S, is unchanged
    // post: true iff element of S has yet to be traversed.

    public Object nextElement();
    // pre: S has more unenumerated elements
    // post: returns another element of S, marks it enumerated
}
A data structure can create an object of type enumeration, which can be used to cycle through the elements. For example, built-in Java class Vector has a method:
  public Enumeration elements()
Can print out the elements of Vector v as follows:
    for (Enumeration enum=v.elements(); enum.hasMoreElements(); )
        System.out.println(enum.nextElement());
Important Notes:
  1. Never change the state of a data structure when doing an enumeration unless you know exactly how the enumeration works as may end up in an infinite loop!
  2. Order of enumeration may be quite different from what you expect. Don't count on a particular order unless you know how the particular enumeration works.
The Bailey book has extended Enumeration interface to Iterator with more functionality:
public interface Iterator extends Enumeration {
    // hasMoreElements(), nextElement() as before

    public void reset();
    // post: the iterator is reset to beginning of traversal

    public Object value();
    // pre: traversal has more elements
    // post: returns current value referenced by the iterator 
}
Now can restart an iterator (rather than creating a new one) and also access a value without moving forward: value().

The data structures in the structure library typically return an Iterator rather than an Enumeration.

We can now rewrite the for loop above in a slightly different manner:

    for (Iterator iter = v.elements(); iter.hasMoreElements(); iter.nextElement())
        System.out.println(iter.value());
This is now a bit clearer in showing how changes occur during each iteration of loop.

Examples of iterators

The text provides an example of the Vector Iterator. Let's look at CircularList iterator instead.

 Have elements method in CircularList:

    public Iterator elements()
    // post: returns iterator to traverse list elements.
    {
        return new CircularListIterator(tail);
    }
Let's see how code actually looks:
class CircularListIterator implements Iterator
{
    protected SinglyLinkedListElement tail;
    protected SinglyLinkedListElement current;
        // element ready to look at next

    public CircularListIterator(SinglyLinkedListElement t)
    // pre: t is a reference to a circular list element
    // post: constructs iterator for traversing circular list
    {
        tail = t;
        reset();
    }

    public void reset()
    // post: resets iterator to point to head of list
    {
        if (tail == null) 
            current = null;
        else 
            current = tail.next();
    }

    public boolean hasMoreElements()
    // post: returns true if some elements not visited
    {
        return current != null;
    }

    public Object nextElement()
    // pre: hasMoreElements()
    // post: returns current element, increments iterator
    {
        Object result = current.value();
        if (current == tail) 
            current = null;
        else 
            current = current.next();
        return result;
    }

    public Object value()
    // pre: hasMoreElements()
    // post: returns current value
    {
        return current.value();
    }
}
Notes:
  1. CircularListIterator is not public. Therefore only available inside package structure. Fine, since only want it to be accessible from method elements() of CircularList. Could also define as an inner class.
  2. Some iterators take a copy of the entire data structure, while others, like CircularList, take only the parts they need (in this case the tail). For example elements method of Vector has body:

  3. return new VectorIterator(this);
  4. Notice constructor remembers info passed in and then calls reset. This is usual pattern for constructors of iterators.
  5. Before using Iterator, be sure to call hasMoreElements() to make sure you don't try to access something that isn't there!
Iterators are useful for printing out all elements of a data structure:
    for (Iterator iter = list.elements(); iter.hasMoreElements(); 
                                                    iter.nextElement())
        System.out.println(iter.value());
Recall again that Iterators are great for traversing a data structure, but should not be used to change the list as the results may not be predictable!

Ordered Structures

We will often want to order Objects. Ordering objects implies that we have a mechanism for comparing them.

Base types can all be compared in the usual ways. But what about objects?

  1. equals function does a byte-for-byte comparison (unless redefined).
  2. == compares references.
Recall that when we discussed algorithms for sorting arrays, we said that the elements of the arrays had to be Comparable. Bailey, in the structure package, defines an interface Comparable.
public interface Comparable {
    public int compareTo(Object item);
    // pre: item is non-null
    // post: returns value < 0 if this < item; 0 if =; > 0 if this > item
}
Let's consider how the Comparable interface might be of use in defining objects that can be placed into an ordered structure. In particular, let's begin by considering a Comparable Association. Recall from Chapter 1:
public class Association {
    protected Object theKey;   // key of the key-value pair
    protected Object theValue; // value of the key-value pair

    public Association(Object key, Object value)
    // pre: key is non-null.
    // post: constructs a key-value pair.
    {
                Assert.pre(key != null, "Key must not be null.");
                theKey = key;
                theValue = value;
    }

    public boolean equals(Object other)
    // pre: other is non-null Association
    // post: returns true iff the keys are equal
    {
                Association otherAssoc = (Association)other;
                return key().equals(otherAssoc.key());
    }
       
    public Object value()
    // post: returns value from association
    {
                        return theValue;
    }

    public Object key()
    // post: returns key from association
    {
                        return theKey;
    }

    public void setValue(Object value)
    // post: sets association's value to value.
    {
                        theValue = value;
    }

    public String toString()
                ...
}

Notice the type-cast that was needed in equals. Because equals in Object takes a parameter of type Object, overridden method must take a parameter of the same type.

ComparableAssociation extends Association:

public class ComparableAssociation extends Association implements Comparable {
    public ComparableAssociation(Comparable key, Object value)
    // pre: key is non-null
    // post: constructs association of key and a value
    {
                super(key,value);
    }

     public ComparableAssociation(Comparable key)
    // pre: key is non-null
    // post: constructs association of key and null
    {
                this(key,null);
    }

    public int compareTo(Object other)
    // pre: other is non-null ComparableAssociation
    // post: returns integer representing relation between values
    {
       ComparableAssociation that = (ComparableAssociation)other;
       Comparable thisKey = (Comparable)this.key();
       Comparable otherKey = (Comparable)that.key();

       return thisKey.compareTo(thatKey);
    }

    public String toString()
                ...
}
Note how compareTo must perform a type cast on its parameter. Again the parameter must be of type Comparable since compareTo in Comparable does. Yes, it's annoying!

Now Associations may be compared and placed in an ordered structure, as long as they are ComparableAssociations. Note that the Comparable interface is defined in Java 1.2.

Please read the rest of this chapter on your own. It discusses ordered vectors and ordered lists. You should understand the details of the implementations (see the code in the structures library as well as the material in the text).