Return to the lecture notes index

Lecture #6 (September 12, 2002)

Linked Lists and the Departmental Exam

The department continues to develop the end-of-semester exam which will involve implementing a method within a linked list to modify the order of the list, odd, or delete nodes.

As the development continues, we are learning more about the exam and its infrastructure. This week at the "Intro Group Meeting", we discussed and developed some ideas for the exam's linked list class. The implementation details were a little different than those we discussed last class.

So, we're going to shift gears a little bit and do it using a design similar to the "common format". As the "common format" continues to be revised, I'll keep you posted.

For today, we're pulling the Node class out of the LinkedList class so that Java will protect its private members from misuse by the LinkedList, even though that exposes the Node class to the world.

Today's Class

During today's lecture, we discussed and implemented the following code for the linked list and node. We'll continued next class.

The LinkedListNode Implementation

 
public class LinkedListNode 
{
  /*
   * We made the data Comparable, so that we can do things such
   * as put nodes in order, &c.
   *
   * The comparable interface specifies one method, 
   * "int compareTo (Object o)"
   *
   * Considering x.compareTo(y):
   *     0, if x=y
   *    -1, if x y
   *
   * If it helps, the sign is the same as (x-y)
   */
  private Comparable data;  // The items stored in the list
  private LinkedListNode next; // The name of the next node in the list
  
  public LinkedListNode()  
  {
    // Just initialize everything to null -- don't know any better
    data = null;
    next = null;
  }  
  
  public LinkedListNode (Comparable data, LinkedListNode next) 
  {
   // Initialize to what we are given
   this.data = data; 
   this.next  = next;
  }
  
  public LinkedListNode (Comparable data)
  {
    // Init to given data, but set next to null. 
    this.data = data;
    this.next = null;
  }
  
  /*
   * The next few are pretty straight-forward
   */
  public void setNext (LinkedListNode next)
  {
    this.next = next;
  }
  
  public Comparable getData()
  {
    return data;
  }
  
  public LinkedListNode getNext()
  {
    return next;
  }

  /*
   * Notice not setData(Comparable)
   *
   * The reason is that we're considering the Node to be a container
   * for a particular object. We can move it around, or ask it to
   * change itself. But, we can't "swap objects". This avoids
   * many errors. The object, can still change itself, unless,
   * like a String, it is immutable
   */
}
  

The LinkedList Implementation

public class LinkedList
{
  private LinkedListNode head; // First node in list
  private LinkedListNode tail; // Last node in list
  private LinkedListNode index; // Lets user move through list
  

  // Give us our own class to trow errors. Must be public, so
  // they can be caught outside of the class.
  public class LinkedListException extends Exception
  {
    public LinkedListException (String msg)
    {
      super(msg); //This calls the constructor of the Exception parent class
    }
  }
  
  // Easy enough
  public LinkedList()
  {
    head = tail = index = null;
  }
  

  // Add a node to the beginning of the list, before the first node
  public void prepend (Comparable data)
  {
    // Wow! Create the new node, with the old head as its successor,
    // and assign the new head to the new node. Perfect.
    head = new LinkedListNode (data, head);
    
    // But, if the list is empty, we need to set the tail
    if (null == tail)
      tail = head;
      
    // This is a bit of a semantic question. But we decided to set the index.
    if (null == index)
      index = head;
  }
  

  // Add after the last node in the list.
  public void append (Comparable data)
  {

    // Is the list empty? 
    if (null == tail)
      // If it is, then just like prepend, really.
      head = index = tail = new LinkedListNode(data);
    else
    {
      // Create the new node with the data and make the old tail reference it
      tail.setNext (new LinkedListNode (data));

      // Then advance the old tail
      tail = tail.getNext();
    }
  }
  
  // Make index name the first node.
  // The index will basically be an iterator. 
  // We could have defined our own iterator class, like the Java API,
  // but this is more compact (even if less uniform)
  public void resetIndex()
  {
    index = head;
  }
  
  // Return the value of the data stored at the index'ed node
  public Comparable getIndex() throws LinkedListException
  {
    // If there is no indexed node (empty list, at end, &c), yell!
    if (null == index)
      throw new LinkedListException ("Null index in getIndex()");
   
    // Otherwise, just return it
    return index.getData();
  }
  

  // Move the index to the next node
  public void advanceIndex() throws LinkedListException
  {
    // If the index is null, or if we can't advance it, yell!
    if ((index == null) || (null == index.getNext()))
      throw new LinkedListException ("Null index in advanceIndex()");
      
    // Send it forward.
    index = index.getNext();
  }
}