next up previous contents
Next: Ports and interaction with Up: Interfaces: Applications Previous: Callback functions   Contents

Iterators

A linear list is a generic way of storing Objects of any type. We might define a class to store generic linear lists:

  class Linearlist {
    // Implement list as an array with arbitrary upper bound limit
    private int limit = 100;      
    private Object[] data = new Object[limit]; 
    private int size;  // Current size of the list

    public Linearlist(){  // Constructor
      size = 0;
    }
 
    public void append(Object o){
      data[size] = o;
      size++;
      if (size > data.length){ // Get more space!
        Object[] tmp = data;   // Save a pointer to current array
        limit *= 2;  
        data = new Object[limit]; // New array of double the size
        for (i = 0; i < tmp.length; i++){
          data[i] = tmp[i];
        }
    }
    ...
  }

Alternately, we might modify the internal implementation to use a linked list rather than an array.

  class Node{
    private Object data;
    private Node next;

    public Node(Object o){
      data = o;
      next = null;
    }
  }

  class Linearlist {

    private Node head;    // Implement list as a linked list
    private int size;

    public Linearlist(){  // Constructor
      size = 0;
    }
 
    public void append(Object o){
      Node m;

      // Locate last node in list and append a new node containing o
      for (m = head; m.next != null; m = m.next){}
      Node n = new Node(o);
      m.next = n;
      
      size++;
    }
    ...
  }

Now, what if we want to externally run through a Linearlist from beginning to end? If we had access to the array data we could write

  int i;
  for (i = 0; i < data.length; i++){
    ... // do something with data[i]
  }

Alternatively, if we had access to head, we could write

  Node m;
  for (m = head; m != null; m = m.next}
    ... // do something with m.data
  }

However, we have access to neither. In fact, we do not even know which of the two implementations is in use! The Linearlist class should add functionality to support such a list traversal. Abstractly we need a way to do the following:

  Start at the beginning of the list;
  while (there is a next element){
    get the next element;
    do something with it
  }

We can encapsulate this functionality in an interface called Iterator:

  public interface Iterator{
    public abstract boolean has_next();
    public abstract Object get_next();
  }

Now, we need to implement Iterator in Linearlist. If we make this a part of the main class, there will be a single ``pointer'' stored within the class corresponding to the temporary variable i (or m) in the loop we wrote earlier to traverse the list. This means that we will not be able to simulate a nested loop such as the following:

  for (i = 0; i < data.length; i++){
    for (j = 0; j < data.length; j++){
       ... // do something with data[i] and data[j]
    }
  }

because when we execute the equivalent of the inner loop, we lose the position that we were in in the outer loop.

A better solution is to allow Linearlist to create, on the fly, a new object that implements Iterator and return it outside. This object will be an instance of a nested class, defined within Linearlist. It will have access to private information about the list, even though it is invoked outside! Here is how we do it.

  class Linearlist {
    // Implement list as an array with arbitrary upper bound limit
    private int limit = 100;      
    private Object[] data = new Object[limit]; 
    private int size;  // Current size of the list

    public Linearlist(){..}  // Constructor
 
    public void append(Object o){...}

    // A private class to implement Iterator
    private class Iter implements Iterator{
      private int position;

      public Iter(){   // Constructor
        position = 0;  // When an Iterator is created, it points
                       // to the beginning of the list
      }

      public boolean has_next(){
        return (position < data.length - 1);
      }

      public Object get_next(){
        Object o = data[position];
        position++;
        return o;
      }
    }

    // Export a fresh iterator
    public Iterator get_iterator(){
      Iter it = new Iter();    
      return it;
    }
    ...
  }

Now, we can traverse the list externally as follows:

  Linearlist l = new Linearlist();
  ...
  Object o;
  Iterator i = l.get_iterator()

  while (i.has_next()){
    o = i.get_next();
    ...   // do something with o
  }
  ...

What if Linearlist is implemented as a linked list rather than an array? We just change the definition of the class Iter within Linearlist.

    private class Iter implements Iterator{
      private Node position;

      public Iter(){   // Constructor
        // Create a dummy node that points to the head of the list
        position = new Node(null);  
        position.next = head; 
      }

      public boolean has_next(){
        return (position.next != null);
      }

      public Object get_next(){
        position = position.next;
        Object o = position.data;
        return o;
      }
    }

Externally, the loop that we wrote with get_iterator(), has_next() and get_next() works as before.

Once again, we have passed a reference to an object but restricted access to the object using an interface. The Iter object created within Linearlist can (and must) access private variables within Linearlist, but the reference that is passed to this internal object is constrained by the Iterator interface, so this capability cannot be exploited externally.

In fact, we can go one step further and even nest the linked list class as a local class within Linearlist to prevent any accidental misuse. A complete implementation of Linearlist as a linked list might be as follows.

  class Linearlist {

    private Node head;       // Implement list as a linked list
    private int size;

    public Linearlist(){...} //Constructor

    public void append(Object o){...}


    private class Node{
      private Object data;
      private Node next;

      public Node(Object o){
        data = o;
        next = null;
      }
    }

    private class Iter implements Iterator{
      private Node position;

      public Iter(){   // Constructor
        // Create a dummy node that points to the head of the list
        position = new Node(null);  
        position.next = head; 
      }

      public boolean has_next(){
        return (position.next != null);
      }

      public Object get_next(){
        position = position.next;
        Object o = position.data;
        return o;
      }
    }

    // Export a fresh iterator
    public Iterator get_iterator(){
      return new Iter();
    }
    ...
  }

Notice that when we return an iterator, since we do not need to ever use a name for the iterator locally, we can directly pass the reference generated by new, rather than assigning it temporarily to a locally defined reference (such as Iter it in the original version) and returning that local variable.


next up previous contents
Next: Ports and interaction with Up: Interfaces: Applications Previous: Callback functions   Contents
Madhavan Mukund 2004-04-29