Data Structures, Algorithms, & Applications in Java
Chapter 11, Exercise 6

The code for the new class is given below. The test program and output are in the files SortedChainWithHeadAndTail.*. Compare the codes with those for the corresponding methods of SortedChain to see how the use of a head and tail node have simplified some of the operations.


public class SortedChainWithHeadAndTail implements Dictionary
{
   // top-level nested class
   protected static class SortedChainNode
   {
      // data members
      protected Comparable key;
      protected Object element;
      protected SortedChainNode next;

      // constructors
      protected SortedChainNode() {}
     
      protected SortedChainNode(Object theKey, Object theElement)
      {
         key = (Comparable) theKey;
         element = theElement;
      }

      protected SortedChainNode(Object theKey, Object theElement,
                                SortedChainNode theNext)
      {
         key = (Comparable) theKey;
         element = theElement;
         next = theNext;
      }
   }

   // data members of SortedChainWithHeadAndTail
   protected SortedChainNode headerNode;
   protected SortedChainNode tailNode;
   protected int size;

   // constructors
   /** create an empty sorted chain */
   public SortedChainWithHeadAndTail(int initialCapacity)
   {
      headerNode = new SortedChainNode();
      tailNode = new SortedChainNode();
      headerNode.next = tailNode;
      // the default initial value of size is 0
   }

   public SortedChainWithHeadAndTail()
      {this(0);}

   // methods
   /** return true iff the chain is empty */
   public boolean isEmpty()
      {return size == 0;}

   /** return current number of elements in list */
   public int size()
      {return size;}

   /** return element with specified key
     * return null if there is no matching element */
   public Object get(Object theKey)
   {
      // put theKey into tailNode
      tailNode.key = (Comparable) theKey;

      SortedChainNode currentNode = headerNode.next;

      // search for match with theKey
      while (currentNode.key.compareTo(theKey) < 0)
         currentNode = currentNode.next;

      // verify match
      if (currentNode != tailNode && currentNode.key.equals(theKey))
         // yes, found match
         return currentNode.element;

      // no match
      return null;
   }
   
   /** insert an element with the specified key
     * overwrite old element if there is already an
     * element with the given key 
     * return old element (if any) with key theKey */
   public Object put(Object theKey, Object theElement)
   {
      // first put theKey into the tail node
      tailNode.key = (Comparable) theKey;

      SortedChainNode currentNode = headerNode;
   
      // move currentNode so that theElement can be inserted after currentNode
      while (currentNode.next.key.compareTo(theKey) < 0)
         {currentNode = currentNode.next;}
   
      // check if there is a matching element
      if (currentNode.next != tailNode && currentNode.next.key.equals(theKey))
      {// replace old element
         Object elementToReturn = currentNode.next.element;
         currentNode.next.element = theElement;
         return elementToReturn;
      }
   
      // insert just after currentNode
      currentNode.next = new SortedChainNode(theKey, theElement,
                                              currentNode.next);
      size++;
      return null;
   }

   /** return matching element and remove it
     * return null if no matching element */
   public Object remove(Object theKey)
   {
      // first put theKey into the tail node
      tailNode.key = (Comparable) theKey;

      SortedChainNode currentNode = headerNode;
   
      // search for matching element
      while (currentNode.next.key.compareTo(theKey) < 0)
         {currentNode = currentNode.next;}
   
      // check if there is a matching element
      if (currentNode.next != tailNode && currentNode.next.key.equals(theKey))
      {// there is a matching element, save in elementToReturn
       // and remove from chain
         Object elementToReturn = currentNode.next.element;
         currentNode.next = currentNode.next.next;
         size--;
         return elementToReturn;
      }
   
      // no matching element to remove
      return null;
   }
   
   /** convert to a string */
   public String toString()
   {
      StringBuffer s = new StringBuffer("["); 
      SortedChainNode currentNode = headerNode.next;
      if (currentNode != tailNode)
      {// nonempty list
         // do first element
         s.append(currentNode.element.toString());
         currentNode = currentNode.next;
         while (currentNode != tailNode)
         {
            s.append(", " + currentNode.element.toString());
            currentNode = currentNode.next;
         }
      }
      s.append("]");

      // create equivalent String
      return new String(s);
   }

   /** create and return an iterator */
   public Iterator iterator()
      {return new SortedChainIterator();}

   /** sorted chain iterator */
   private class SortedChainIterator implements Iterator
   {
      // data member
      private SortedChainNode nextNode;
   
      // constructor
      public SortedChainIterator()
         {nextNode = headerNode.next;}
   
      // methods
      /** return true iff list has more elements */
      public boolean hasNext()
         {return nextNode != tailNode;}
   
      /** return next element in list
        * throws NoSuchElementException
        * if there is no next element */
      public Object next()
      {
         if (nextNode != tailNode)
         {
            Object obj = nextNode.element;
            nextNode = nextNode.next;
            return obj;
         }
         else throw new NoSuchElementException
                        ("No next element");
      }

      /** unsupported method */
      public void remove()
      {
         throw new UnsupportedOperationException
                   ("remove not supported");
      }   
   }
}