SDSU CS 696: Advanced OO
Spring Semester, 1997
Doc 16, Iterator and Visitor

To Lecture Notes Index
San Diego State University -- This page last updated Apr 17, 1997

Contents of Doc 16, Iterator and Visitor


Iterator slide # 2
...Concrete vs. Polymorphic Iterators slide # 3
...Using A Smart Pointer to avoid Memory Leaks slide # 4
...Who Controls the iteration? slide # 5
...Who Defines the Traversal Algorithm? slide # 6
...How robust is the iterator? slide # 7
...Additional Iterator Operations slide # 7
Visitor slide # 10
...Who is Responsible for traversing the Structure? slide # 15
...Why not use method overloading? slide # 16
...When to Use Visitor slide # 18




Doc 16, Iterator and Visitor Slide # 1

Iterator


Provides a way to access elements of an aggregate object sequentially without exposing its underlying representation

Java Examples

Vector listOfStudents = new Vector();

// code to add students not shown

Enumeration list = listOfStudents.elements();

while ( list.hasMoreElements() )
     Console.println( list.nextElement() );
     
Hashtable anIndex = new Hashtable();
// code to add elements to the hashtable not shown

Enumeration list = anIndex.keys();

while ( list.hasMoreElements() )
     Console.println( list.nextElement() );


Doc 16, Iterator and Visitor Slide # 2
Issues

Concrete vs. Polymorphic Iterators


Concrete

Vector listOfStudents = new Vector();

// code to add students not shown

VectorIterator list = new VectorIterator( listOfStudents );

while ( list.hasMoreElements() )
     Console.println( list.nextElement() );


Polymorphic

Vector listOfStudents = new Vector();

// code to add students not shown

Enumeration list = listOfStudents.elements();

while ( list.hasMoreElements() )
     Console.println( list.nextElement() );


Polymorphic iterators can cause problems with memory leaks in C++ because they are on the heap!


Doc 16, Iterator and Visitor Slide # 3

Using A Smart Pointer to avoid Memory Leaks


template <class Item>
class SmartPointer
     {
     public:
          SmartPointer( <Item>* aPointee );
          ~SmartPointer( ) 
               { delete pointee; }
               
          <Item>* operator->() 
               { return pointee; }
          
          <Item>& operator*()
               { return *pointee; }
     
     private:
          // Can not allow multiple copies of pointee
          // hide copy and assignment
          SmartPointer( const SmartPointer& );
          SmartPointer& operator=(const  SmartPointer& );
          
          <Item>* pointee;
     }
     
void sample()
     {
     SmartPointer safe( new Vector() );
     safe->append( "This should work" );
     }


Doc 16, Iterator and Visitor Slide # 4

Who Controls the iteration?

External (Active)

Vector listOfStudents = new Vector();

// code to add students not shown

VectorIterator list = new VectorIterator( listOfStudents );

while ( list.hasMoreElements() )
     Console.println( list.nextElement() );


Internal (Passive)

Vector listOfStudents = new Vector();

// code to add students not shown

while ( listOfStudents.hasMoreElements() )
     Console.println( listOfStudents.nextElement() );


Doc 16, Iterator and Visitor Slide # 5

Who Defines the Traversal Algorithm?

Object being Iterated

Iterator can store where we are

In a Vector this could mean the index of the current item

In a tree structure it could mean a pointer to current node and stack of past nodes

BinaryTree searchTree = new BinaryTree();

// code to add items not shown

Iterator aSearch = searchTree.getIterator();
Iterator bSearch = searchTree.getIterator();
Object first = searchTree.nextElement( aSearch );
Object stillFirst = searchTree.nextElement( bSearch );

Iterator

Makes it easier to have multiple iterator algorithms on same type

On Vector class, why not have a reverseIterator which goes backwords?

In a complex structure the iterator may need access to the iteratee's implementation


Doc 16, Iterator and Visitor Slide # 6

How robust is the iterator?


What happens when items are added/removed from the iteratee while an iterator exists?

Vector listOfStudents = new Vector();

// code to add students not shown

VectorIterator list = new VectorIterator( listOfStudents );

list.removeElementAt( 5 );

while ( list.hasMoreElements() )
     Console.println( list.nextElement() );

Additional Iterator Operations


previous()
back up one location
add( Object item)
add item to the iteratee at current location
remove()
remove the current item from the iteratee
skipTo( some location, item or condition )
go to the location indicated
mark()
mark current location for future return

Doc 16, Iterator and Visitor Slide # 7
Example of Additional Iterator Operations

class SelectIterator
     {
     Enumeration iteratee;
     
     public SelectionIterator( Vector newIteratee )
          {
          iteratee = newIteratee.elements();
          }
     
     public select( Command condition )
          {
          Vector selected = new Vector();
          
          while ( iteratee.hasMoreElements() )
               {
               Object candidate = iteratee.nextElement();
               if ( condition.execute( candidate ) )
                    selected.addElement( candidate )
               }
          return selected;
          }
     }


Doc 16, Iterator and Visitor Slide # 8
class StringContains extends Command
     {
     String searchString;
     public StringContains( String aString )
          {
          searchString = aString;
          }
     
     public boolean execute( Object aString )
          {
          return ((String) aString)).contains( searchString );
          }
     }


Vector studentList = new Vector();
// code adding student not shown

SelectionIterator findName = new SelectionIterator( studentList );

Vector theBills = findName.select( new StringContains( "bill" ) );
Vector theJoses = findName.select( new StringContains( "jose" ) );

Smalltalk Equivalent of Entire Example
Vector studentList = new Vector();
// code adding student not shown

Vector theBills, theJoses;
theBills = studentList.select( [ ( x ) | return x.contains("bill") ]);
theJoses = studentList.select( [ ( x ) | return x.contains("jose") ]);

Doc 16, Iterator and Visitor Slide # 9

Visitor

Example - Tree Structure

class BinaryNode extends Node
     {
     private Node left = null;
     private Node right = null;
     private Object data;

     public void setLeftChild( Node addMe )
          {     left = addMe;     }
     
     public void setRightChild( Node addMe )
          {     right = addMe;     }

     public void setData( Object newData)
          {      data = newData; }

     public String print()
          {
          return left.print() + data.toString() + right.print();
          }
     }

What about preorder visit, postorder visit?
What about an HTML print?
What about printing a 2D representation?
What if this was an expression tree - evaluation?
What if this was a binary search tree - adding, deleting
What about balancing the binary search tree -
AVL
Red-Black

Doc 16, Iterator and Visitor Slide # 10
Iterators can solve some of the Problems

class PreorderIterator
     {
     private Node currentNode;
 
     public PreorderIterator( Node aNode )
          { currentNode = aNode; }

     // lots of stuff not shown
     }

class PostorderIterator
     { blah }


Node root = new BinaryNode();

// code adding nodes not shown

PreorderIterator treeList = new PreorderIterator( root );

while ( treeList.hasMoreElements() )
     Console.print( treeList.nextElement() );


Doc 16, Iterator and Visitor Slide # 11
Visitor Pattern Sends a object to Each Item

abstract class Node
     {
     private Object data = null;

     abstract public void accept( NodeVisitor visitor );

     public void setData( Object newData )
          {  data = newData;     }

     public void getData( )     { return data; }
     }          

class BinaryNode extends Node
     {
     private Node left = null;
     private Node right = null;
     
     public void setLeftChild( Node addMe )      { left = addMe;}
     
     public void setRightChild( Node addMe )      { right = addMe; }

     public void getLeftChild()       { return left; }
     
     public void getRightChild()     { return right;     }
     
     public void accept( NodeVisitor visitor )
          {  
          left.accept( visitor);
          right.accept( visitor);
          visitor.visitBinaryNode( this ); } 
     }

Doc 16, Iterator and Visitor Slide # 12

class Leaf extends Node
     {
     public void accept( NodeVisitor visitor )
          {
          visitor.visitLeaf( this ); 
          }
     }

class SingleNode extends Node
     {
     private Node child = null;
     
     public void setChild( Node addMe ) { child = addMe; }

     public void getChild() { return child; }

     public void accept( NodeVisitor visitor )
          {
          child.accept( this );
          visitor.visitSingleNode( this );
          } 
     }

abstract class NodeVisitor
     {
     abstract public void visitLeaf( Leaf toVisit);
     abstract public void visitBinaryNode( BinaryNode toVisit);
     abstract public void visitSingleNode( SingleNode toVisit);
     }


Doc 16, Iterator and Visitor Slide # 13
class ExpressionEvaluator extends NodeVisitor
     {
     Stack values = new Stack();
     
     public void visitLeaf( Leaf toVisit)
          {  values.push( toVisit.getData() );  }
          
     public void visitBinaryNode( BinaryNode toVisit)
          {
          Number rightOperand = (Number) values.pop();
          Number leftOperand = (Number) values.pop();
          Number result;
          
          char operator = (char) toVisit.getData();
          switch ( operator )
               {
               case '+': result = leftOperand + rightOperand;break;
               case '/': result = leftOperand / rightOperand;break;
               }
          values.push( result );
          }
     
     public void visitSingleNode( SingleNode toVisit)
          {
          Number operand = (Number) values.pop();
          Number result;
          String operator = (String) toVisit.getData();
          switch ( operator )
               {
               case "-": result =  -1 * operand; break;
               case "++": result = operand + 1; break;
               }
          values.push( result );
     }


Doc 16, Iterator and Visitor Slide # 14
To Use the Visitor

Node root = new BinaryNode();

// code adding nodes not shown

ExpressionEvaluator aVisitor = new ExpressionEvaluator();
root.accept( aVisitor );



Issues

Who is Responsible for traversing the Structure?


The above example does this.

This separates the traversing algorithm from the structure and the visitor

Use if the traversal algorithm depends on the result of operation the visitor performs


Doc 16, Iterator and Visitor Slide # 15

Why not use method overloading?


abstract class Node
     {
     private Object data = null;

     abstract public void accept( NodeVisitor visitor )
          { visitor.visit( this ); }

     public void setData( Object newData ) {  data = newData;     }
     public void getData( )     { return data; }
     }          

class BinaryNode extends Node
     {
     private Node left = null;
     private Node right = null;
     
     public void setLeftChild( Node addMe )      { left = addMe;}
     public void setRightChild( Node addMe )      { right = addMe; }
     public void getLeftChild()       { return left; }
     public void getRightChild()     { return right;     }
     }
abstract class NodeVisitor
     {
     abstract public void visit( Leaf toVisit);
     abstract public void visit( BinaryNode toVisit);
     abstract public void visit( SingleNode toVisit);
     }

class SingleNode extends Node
     {
     private Node child = null;
     public void setChild( Node addMe ) { child = addMe; }
     public void getChild() { return child; }
     }
Doc 16, Iterator and Visitor Slide # 16
class ExpressionEvaluator extends NodeVisitor
     {
     Stack values = new Stack();
     
     public void visit( Leaf toVisit)
          {  values.push( toVisit.getData() );  }
          
     public void visit( BinaryNode toVisit)
          {
          // Same as visitBinaryNode in earlier example
           }
     
     public void visit( SingleNode toVisit)
          {
          Number operand = (Number) values.pop();
          Number result;
          String operator = (String) toVisit.getData();
          switch ( operator )
               {
               case "-": result =  -1 * operand; break;
               case "++": result = operand + 1; break;
               }
          values.push( result );
     }


Note this example assumes traversal is in an iterator


Doc 16, Iterator and Visitor Slide # 17

When to Use Visitor