import rebelsky.util.List; import rebelsky.util.BinaryNode; import rebelsky.util.ListException; import rebelsky.util.EmptyListException; import rebelsky.util.Unimplemented; /** * A simple implementation of linked lists. Currently implements * singly linked lists (and therefore only a portion of the many * possible list functions). This implementation does ensure that * the cursor is always somewhere in the list. Hence, * the cursor-related functions are less likely to throw exceptions. *
* Copyright (c) 1998 Samuel A. Rebelsky. All rights reserved.
*
* @version 1.1 of March 1998
* @author Samuel A. Rebelsky
*/
public class LinkedList
implements List
{
// +--------+--------------------------------------------------
// | Fields |
// +--------+
/**
* The number of elements in the list.
*/
protected int length;
/**
* The front of the list.
*/
protected BinaryNode front;
/**
* The back of the list.
*/
protected BinaryNode back;
/**
* The "current" element in the list.
*/
protected BinaryNode cursor;
// +--------------+--------------------------------------------
// | Constructors |
// +--------------+
/**
* Create a new empty linked list.
*/
public void LinkedList() {
clear();
} // LinkedList()
// +-----------------------+-----------------------------------
// | Information retrieval |
// +-----------------------+
/**
* Get the element referred to by the cursor.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The first element in the list is returned.
*
Postcondition:
* The list is not modified.
*
Postcondition:
* The cursor is not affected.
*
* @exception EmptyListException
* if the list is empty.
*/
public Object getCurrent()
throws EmptyListException
{
if (empty()) {
throw new EmptyListException();
}
return cursor.getContents();
} // getCurrent()
/**
* Get the first element in the list.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The first element in the list is returned.
*
Postcondition:
* The list is not modified.
*
* @exception EmptyListException
* if the list is empty.
*/
public Object getFirst()
throws EmptyListException
{
if (empty()) {
throw new EmptyListException();
}
return front.getContents();
} // getFirst()
/**
* Get the last element in the list.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The last element in the list is returned.
*
Postcondition:
* The list is not modified.
*
* @exception EmptyListException
* if the list is empty.
*/
public Object getLast()
throws EmptyListException
{
if (empty()) {
throw new EmptyListException();
}
return back.getContents();
} // getLast()
/**
* Determine whether the list is empty.
*
Precondition:
* (none)
*
Postcondition:
* The list is unaffected.
*/
public boolean empty()
{
// The list is empty if the front does not refer to an element.
return (front == null);
} // empty()
/**
* Determine the number of elements in the list.
*
Precondition:
* (none)
*
Postcondition:
* The list is uanffected.
*
Postcondition:
* The number of elements in the list is returned.
*/
public int length()
{
return length;
} // length()
// +--------------------+--------------------------------------
// | Inserting elements |
// +--------------------+
/**
* Add an element at the front of the list. If the list is
* empty, creates a one-element list.
*
Precondition:
* (none)
*
Postcondition:
* The element is now at the front of the list.
*
Postcondition:
* The list increases in length by one.
*
Postcondition:
* If the list was not previously empty, the cursor is
* unchanged (if it previously referred to the front of the list,
* it now refers to the second element of the list).
*
Postcondition:
* If the list was previously empty, the list now has one element
* which is front, back, and cursor.
*/
public void addAtFront(Object elt)
{
// Handle the empty list
if (empty()) {
newList(elt);
}
// Handle nonempty lists
else {
// Create the new front with the apprpropriate contents and
// with the old front as the next element.
BinaryNode new_front = new BinaryNode(elt, front);
front = new_front;
// Update the length
length = length + 1;
}
} // addAtFront(Object)
/**
* Add an element at the end of the list. If the list is
* empty, creates a one-element list.
*
Precondition:
* (none)
*
Postcondition:
* The element is now at the end of the list.
*
Postcondition:
* The list increases in length by one.
*
Postcondition:
* If the list was not previously empty, the cursor is
* unchanged (if it previously referred to the end of the list,
* it now refers to the penulitimate element of the list).
*
Postcondition:
* If the list was previously empty, the list now has one element
* which is front, back, and cursor.
*/
public void addAtEnd(Object elt)
{
// Handle the empty list
if (empty()) {
newList(elt);
}
// Handle nonempty lists
else {
// Create the new front with the apprpropriate contents and
// no subsequent element.
BinaryNode new_back = new BinaryNode(elt);
back.setNext(new_back);
back = new_back;
// Update the length
length = length + 1;
}
} // addAtEnd(Object)
/**
* Add an element before the cursor.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The element is inserted before the cursor.
*
Postcondition:
* The list increases in length by one.
*
Postcondition:
* The cursor has not changed.
*
* @exception EmptyListException
* if the list is empty.
* @exception ListException
* if some implementation-related error occurs.
* @exception Unimplemented
* until this implementation supports this method.
*/
public void addBeforeCurrent(Object elt)
throws EmptyListException, ListException, Unimplemented
{
throw new Unimplemented();
} // addBeforeCurrent(Object)
/**
* Add an element after the cursor.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The element is inserted after the cursor .
*
Postcondition:
* The list increases in length by one.
*
Postcondition:
* The cursor has not moved.
*
* @exception EmptyListException
* if the list is empty.
*/
public void addAfterCurrent(Object elt)
throws EmptyListException
{
// Handle the empty list
if (empty()) {
throw new EmptyListException();
}
// Handle nonempty lists
else {
// Create a new node whose contents is the given element and
// whose subsequent element is the old successor to the cursor
// element.
BinaryNode new_elt = new BinaryNode(elt, cursor.getNext());
// Update the successor of the cursor
cursor.setNext(new_elt);
// Update the length
length = length + 1;
// Handle the special case of the cursor being the last element (in
// which case the newly added element is now the last element).
if (cursor == back) {
back = new_elt;
} // if (cursor == back)
} // nonempty list
} // addAfterCurrent(Object)
// +-------------------+---------------------------------------
// | Deleting elements |
// +-------------------+
/**
* Delete all the elements in the list.
*
Precondition:
* (none)
*
Postcondition:
* The list is empty (contains no elements).
*/
public void clear()
{
length = 0;
front = null;
back = null;
cursor = null;
} // clear()
/**
* Delete the element referred to by the cursor.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The element referred to by the cursor is deleted.
*
Postcondition:
* The list decreases in length by one.
*
Postcondition:
* If the cursor was at the end of the list, it is now at the front
* of the list. Otherwise, it has advanced to the next element of
* the list.
*
* @return the deleted element.
* @exception EmptyListException
* if the list is empty.
* @exception ListException
* if some implementation-related error occurs.
* @exception Unimplemented
* until this implementation supports this method.
*/
public Object deleteCurrent()
throws EmptyListException, ListException, Unimplemented
{
throw new Unimplemented();
} // deleteCurrent()
/**
* Delete the first element of the list.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The first element is deleted.
*
Postcondition:
* The list decreases in length by one.
*
Postcondition:
* If the cursor was at the front of the list, it is now at the
* new front of the list. Otherwise, it is unchanged.
*
* @return the deleted element
* @exception EmptyListException
* if the list is empty.
*/
public Object deleteFirst()
throws EmptyListException
{
// Handle the empty list
if (empty()) {
throw new EmptyListException();
}
// Grab the old front
BinaryNode tmp = front;
// Deal with the cursor being at the front of the list
if (cursor == front) {
cursor = front.getNext();
}
// Advance the front of the list
front = front.getNext();
// And update the length
length = length - 1;
// Return the contents of the old front.
return tmp.getContents();
} // deleteFirst()
/**
* Delete the last element of the list.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The last element is deleted.
*
Postcondition:
* The list decreases in length by one.
*
Postcondition:
* If the cursor was at the end of the list, it is now at the new
* last element. Otherwise, it is unchanged.
*
* @return the deleted element.
* @exception EmptyListException
* if the list is empty.
* @exception ListException
* if some implementation-related error occurs.
* @exception Unimplemented
* until this implementation supports this method.
*/
public Object deleteLast()
throws EmptyListException, ListException, Unimplemented
{
throw new Unimplemented();
} // deleteLast()
// +------------------+----------------------------------------
// | Standard Methods |
// +------------------+
/**
* Develop a string version of the list.
*/
public String toString()
{
// Deal with the empty list.
if (empty()) {
return "()";
}
// Nonempty lists: Start with open paren, follow with elements
// separated by spaces
String str = "(";
BinaryNode tmp = front;
// Keep going until we reach the end of the list
while (tmp != back) {
str = str + tmp.getContents().toString() + " ";
tmp = tmp.getNext();
} // while
// Add the last element
str = str + tmp.getContents() + ")";
// That's it
return str;
} // toString()
// +-------------------------+---------------------------------
// | Manipulating the cursor |
// +-------------------------+
/**
* Reset the cursor to the beginning of the list.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* The cursor is at the beginning of the list.
*
Postcondition:
* The list is not modified.
*
* @exception EmptyListException
* if the list is empty.
*/
public void reset()
throws EmptyListException
{
if (empty()) {
throw new EmptyListException();
}
cursor = front;
} // reset()
/**
* Advance the cursor.
*
Precondition:
* The list is nonempty.
*
Precondition:
* The cursor is not at the end of the list.
*
Postcondition:
* The cursor is advanced to the next element.
*
Postcondition:
* The list is not modified.
*
* @exception EmptyListException
* if the list is empty.
* @exception ListException
* If the cursor is at the end of the list.
*/
public void advance()
throws EmptyListException, ListException
{
if (empty()) {
throw new EmptyListException();
}
if (cursor.getNext() == null) {
throw new ListException("Cannot advance beyond end of list");
}
cursor = cursor.getNext();
} // advance()
/**
* Move the cursor backwards one position.
*
Precondition:
* The list is nonempty.
*
Precondition:
* The cursor is not at the beginning of the list.
*
Postcondition:
* The cursor is now at the previous element.
*
Postcondition:
* The list is not modified.
*
* @exception EmptyListException
* if the list is empty.
* @exception ListException
* if some implementation-related error occurs or the cursor
* is at the beginning of the list.
* @exception Unimplemented
* until this implementation supports this method.
*/
public void backup()
throws EmptyListException, ListException, Unimplemented
{
throw new Unimplemented();
} // backup()
/**
* Advance the cursor to the first element equal to obj after (or
* including) the cursor. If the cursor is not equal to obj and there
* are no subsequent instances of obj, then return false and leave
* the cursor where it was.
*
Precondition:
* The list is nonempty.
*
Postcondition:
* If there is an appropriate element in the list (equal to
* obj and after or including the cursor) then the cursor
* is at the first such object.
*
* @return true if an element is found; false otherwise.
* @exception EmptyListException
* if the list is empty.
* @exception ListException
* if some implementation-related error occurs.
* @exception Unimplemented
* until this implementation supports this method.
*/
public boolean find(Object obj)
throws EmptyListException, ListException, Unimplemented
{
throw new Unimplemented();
} // find(Object)
// +-----------------+-----------------------------------------
// | Local Utilities |
// +-----------------+
/**
* Make a one-element list. Since I control who can call this, I don't
* need to worry as much about exceptions.
*
Precondition:
* The list is empty.
*
Postcondition:
* The list contains exactly one element.
*/
protected void newList(Object elt)
{
// Create a new node
BinaryNode new_node = new BinaryNode(elt);
// Make everything refer to it
front = new_node;
back = new_node;
cursor = new_node;
// Set the length
length = 1;
} // newList(Object)
} // List