Return to the lecture notes index

Lecture 7 (May 25, 2005)

Implementing A Node

Before we can create a linked list, we first need to create a class to represent one piece of the list, called a node. A node consists of an Object to store the data, and a reference to the next Node in the list.

Since a Node is only useful as a part of a linked list, we can make the implementation of the Node class part of the implementation of the LinkedList. When one class is defined inside another class, we call this a subclass. So, we can define the Node class as a subclass of the LinkedList class. This insures that no one outside of the LinkedList class can use a Node by itself.

Implementing A Linked List

Now that we have a node, how do we construct a linked list? Since each node stores a reference to the next one in the list, we at least need a reference to the beginning of the list. We call this reference the "head" of the list, and from the head we can get to any other node in the list. For convenience, we will also store a reference to the end of the list, called the "tail". This saves us the time it takes to get from the beginning to the end of the list when we need to operate on the last node.

For another level of convenience, we will create a reference called "index", which will be able to move along the nodes in the list. The index allows us to operate on a specific spot in the list without have to walk the list to get to that spot.

The "LinkedList" Class

The following gives a definition for a LinkedList class. It includes subclasses Node, which represents one piece of the list, and IndexException, which is an Exception which can be thrown if there is a problem using the index reference.


/*
 * This class defines a LinkedList.  It includes
 * subclasses Node and IndexException.  The class
 * stores a reference to the head and tail of the
 * list, and also an index reference to the current
 * position in the list.
 */
class LinkedList
{
	/*
	 * This class defines an Exception which can be
	 * thrown in the case of an error involving the
	 * "index" reference.  It is a public subclass,
	 * so that other classes can catch it, but have
	 * to explicitly recognize its scope by referring
	 * to it by LinkedList.IndexException
	 */
	public class IndexException extends Exception
	{
		/*
		 * This method creates a String representation
		 * of the Exception which has occurred
		 */
		public String toString()
		{
			return ("Bad index in Linked List");
		}
	}

	/*
	 * This class defines one piece of the linked list.
	 * It contains an Object which represents the data
	 * stored at the current spot, and a reference to
	 * the next Node in the list
	 */
	private class Node
	{
		private Object data;
		private Node next;

		/*
		 * Constructor.  Initializes instance variables
		 * to specified values
		 */
		public Node (Object data, Node next)
		{
			this.data = data;
			this.next = next;
		}

		/*
		 * Constructor.  Initializes data to the
		 * specified value, and the next reference
		 * to null
		 */
		public Node (Object data)
		{
			this.data = data;
			this.next = null;
		}

		/*
		 * Returns the data
		 */
		public Object getData()
		{
			return data;
		}

		/*
		 * Returns reference to next node
		 */
		public Node getNext()
		{
			return next;
		}

		/*
		 * Sets the next reference to the specified value
		 */
		public void setNext(Node next)
		{
			this.next = next;
		}

		/*
		 * Sets the data to the specified value
		 *
		 * We could have chosen not to implement this
		 * method, and instead force users to create
		 * new Node objects when they want to change
		 * the data.
		 */
		public void setData(Object data)
		{
			this.data = data;
		}
	}

	private Node head;
	private Node tail;
	private Node index;

	/*
	 * Constructor.  By default, set all the references
	 * to null
	 */
	public LinkedList()
	{
		head = tail = index = null;
	}

	public void addHead(Object data)
	{
		// Create new node w/data
		Node newNode;
		newNode = new Node(data);

		// Set new node to point to the old head
		newNode.setNext(head);

		// Reset head of list
		head = newNode;

		// Special case: empty list
		if (null == tail)
		{
			tail = head = index = newNode;
		}
	}

	public void addTail(Object data)
	{
		// Create new node w/data
		Node newNode = new Node(data);

		// special case: Empty list
		// can test either head or tail == null
		if (null == head)
		{
			head = tail = index = newNode;
			return; // all done
		}

		// Common case: list is not empty
		tail.setNext(newNode);

		tail = newNode;
	}

	public void resetIndex()
	{
		index = head;
	}

	public Object getIndexedNode()
	{
		// special case: null index
		if (null == index)
		{
			return null;
		}

		// Common case: return indexed value
		return index.getData();
	}

	public void advanceIndex()
	{
		// special case: null index
		if (null == index)
		{
			return;
		}

		// Common case: move forward
		index = index.getNext();
	}

	public void insertAfterIndex(Object data)
		throws IndexException
	{
		// special case: null index
		if (null == index)
		{
			throw new IndexException();
		}

		// Common case
		Node newNode = new Node(data);

		newNode.setNext(index.getNext());

		index.setNext(newNode);

		// BAD mistake:  index = newNode;
	}

	public void deleteAfterIndex()
	{
		// special case 1: null index
		if (null == index)
		{
			return;
		}

		// special case 2: null index.next
		if (null == index.getNext())
		{
			return;
		}

                // special case 3: we are removing the tail
                if (tail == index.getNext())
                {
                        index.setNext(null);
                        tail = index;
                        return;
                }

		// common case
		index.setNext(index.getNext().getNext());

		return;
	}

}