// Implementation of circular lists, using singly linked elements.
// (c) 1998, 2001 duane a. bailey
package structure;
import java.util.Iterator;
//+interface
/**
* An implementation of lists using circularly linked elements,
* similar to that of {@link java.util.LinkedList java.util.LinkedList}.
*
* This class is an implementation of the {@link List} interface.
* Operations accessing or modifying either the head or the tail of
* the list execute in constant time.
* Circular lists are as space-efficient as singly linked lists,
* but tail-related operations are less costly.
*
* Example usage:
*
* To place a copy of every unique parameter passed to a program into a
* CircularList, we would use the following:
*
* public static void main({@link java.lang.String String[]} arguments)
* {
* {@link CircularList} argList = new {@link #CircularList()};
* for (int i = 0; i < arguments.length; i++){
* if (!argList.{@link #contains(Object) contains(arguments[i])}){
* argList.{@link #add(Object) add(arguments[i])};
* }
* }
* System.out.println(argList);
* }
*
* @version $Id: CircularList.java,v 1.2 2005/10/18 19:47:20 terescoj Exp $
* @author, 2001 duane a. bailey
* @see SinglyLinkedList
* @see DoublyLinkedList
*/
public class CircularList extends AbstractList
{
//-interface
//+constructor
//+private
/**
* A reference to tail of list. tail points to head.
*/
protected SinglyLinkedListElement tail;
/**
* Number of elements within circular list.
*/
protected int count;
//-private
//+interface
/**
* Construct an empty circular list.
*
* @pre constructs a new circular list
*/
public CircularList()
//-interface
{
tail = null;
count = 0;
}
//-constructor
//-interface
//+add
/**
* Add an element to head of circular list.
*
* @post adds value to beginning of list
*
* @param value value to be added to list.
*/
public void add(ELTTYPE value)
//-interface
{
addFirst(value);
}
//-add
//+interface
//+addToHead
/**
* Add an element to head of list.
*
* @pre value non-null
* @post adds element to head of list
*
* @param value value added to head of list.
*/
public void addFirst(ELTTYPE value)
//-interface
{
SinglyLinkedListElement temp =
new SinglyLinkedListElement(value);
if (tail == null) { // first value added
tail = temp;
tail.setNext(tail);
} else { // element exists in list
temp.setNext(tail.next());
tail.setNext(temp);
}
count++;
}
//-addToHead
//+interface
//+addToTail
/**
* Add a value to tail of circular list.
*
* @pre value non-null
* @post adds element to tail of list
*
* @param value value to be added.
*/
public void addLast(ELTTYPE value)
//-interface
{
// new entry:
addFirst(value);
tail = tail.next();
}
//-addToTail
//+interface
/**
* Determine if a list is empty.
*
* @pre !isEmpty()
* @post returns value at head of list
*
* @return True if there are no elements within list.
*/
public ELTTYPE getFirst()
//-interface
{
return tail.next().value();
}
//+interface
/**
* Peek at last element of list.
*
* @pre !isEmpty()
* @post returns value at tail of list
*
* @return value of last element of list.
*/
public ELTTYPE getLast()
//-interface
{
return tail.value();
}
//+interface
/**
* Remove a value from head of list.
*
* @pre !isEmpty()
* @post returns and removes value from head of list
*
* @return value removed.
*/
public ELTTYPE removeFirst()
//-interface
{
SinglyLinkedListElement temp = tail.next(); // ie. head of list
if (tail == tail.next()) {
tail = null;
} else {
tail.setNext(temp.next());
temp.setNext(null); // helps clean things up; temp is free
}
count--;
return temp.value();
}
//+interface
//+removeFromTail
/**
* Remove a value from tail of list.
*
* @pre !isEmpty()
* @post returns and removes value from tail of list
*
* @return value removed.
*/
public ELTTYPE removeLast()
//-interface
{
Assert.pre(!isEmpty(),"list is not empty.");
SinglyLinkedListElement finger = tail;
while (finger.next() != tail) {
finger = finger.next();
}
// finger now points to second-to-last value
SinglyLinkedListElement temp = tail;
if (finger == tail)
{
tail = null;
} else {
finger.setNext(tail.next());
tail = finger;
}
count--;
return temp.value();
}
//-removeFromTail
//+interface
/**
* Check if a list contains an element.
*
* @pre value != null
* @post returns true if list contains value, else false
*
* @param value value sought.
* @return True iff value is within list.
*/
public boolean contains(ELTTYPE value)
//-interface
{
if (tail == null) return false;
SinglyLinkedListElement finger;
finger = tail.next();
while ((finger != tail) && (!finger.value().equals(value)))
{
finger = finger.next();
}
return finger.value().equals(value);
}
//+interface
/**
* Remove a value from a list.
*
* @pre value != null
* @post removes and returns element equal to value, or null
*
* @param value value sought.
* @return value removed from list.
*/
public ELTTYPE remove(ELTTYPE value)
//-interface
{
if (tail == null) return null;
SinglyLinkedListElement finger = tail.next();
SinglyLinkedListElement previous = tail;
int compares;
for (compares = 0;
(compares < count) && (!finger.value().equals(value));
compares++)
{
previous = finger;
finger = finger.next();
}
if (finger.value().equals(value)) {
// an example of pigeon-hole principle
if (tail == tail.next()) { tail = null; }
else {
if (finger == tail) tail = previous;
previous.setNext(previous.next().next());
}
// finger value free
finger.setNext(null); // to keep things disconnected
count--; // fewer elements
return finger.value();
} else return null;
}
//+interface
/**
* Determine size of list.
*
* @post returns number of elements in list
*
* @return number of elements in list.
*/
public int size()
//-interface
{
return count;
}
//+interface
/**
* Get value at location i.
*
* @pre 0 <= i < size()
* @post returns object found at that location
*
* @param i position of value to be retrieved.
* @return value retrieved from location i (returns null if i invalid)
*/
public ELTTYPE get(int i)
//-interface
{
if (i >= size()) {
return null;
}
SinglyLinkedListElement finger = tail.next();
// search for ith element or end of list
while (i > 0)
{
finger = finger.next();
i--;
}
// return value found
return finger.value();
}
/**
* Accessor method for tail field
*/
protected SinglyLinkedListElement getTail(){
return tail;
}
/**
* Set value stored at location i to object o, returning old value.
*
* @pre 0 <= i < size()
* @post sets ith entry of list to value o, returns old value
* @param i location of entry to be changed.
* @param o new value
* @return former value of ith entry of list.
*/
public ELTTYPE set(int i, ELTTYPE o)
{
if (i >= size()) return null;
SinglyLinkedListElement finger = tail.next();
// search for ith element or end of list
while (i > 0)
{
finger = finger.next();
i--;
}
// get old value, update new value
ELTTYPE result = finger.value();
finger.setValue(o);
return result;
}
//+midAdd
/**
* Insert value at location.
*
* @pre 0 <= i <= size()
* @post adds ith entry of list to value o
* @param i index of this new value
* @param o value to be stored
*/
public void add(int i, ELTTYPE o)
{
Assert.pre((0 <= i) && (i <= size()),"Index in range.");
if (i == 0) addFirst(o);
else if (i == size()) addLast(o);
else {
SinglyLinkedListElement previous = tail;
SinglyLinkedListElement next = tail.next();
while (i > 0)
{
previous = next;
next = next.next();
i--;
}
SinglyLinkedListElement current =
new SinglyLinkedListElement(o,next);
count++;
previous.setNext(current);
}
}
//-midAdd
/**
* Remove and return value at location i.
*
* @pre 0 <= i < size()
* @post removes and returns object found at that location
*
* @param i position of value to be retrieved.
* @return value retrieved from location i (returns null if i invalid)
*/
public ELTTYPE remove(int i)
{
Assert.pre((0 <= i) && (i < size()),"Index in range.");
// if not in list, return nothing
if (i == 0) return removeFirst();
if (i == size()-1) return removeLast();
SinglyLinkedListElement previous = tail;
SinglyLinkedListElement finger = tail.next(); // ie. head
// count to appropriate location
while (i > 0)
{
i--;
previous = finger;
finger = finger.next();
}
// unlink finger'd object
previous.setNext(finger.next());
count--;
return finger.value();
}
/**
* Determine first location of a value in list.
*
* @pre value is not null
* @post returns (0-origin) index of value,
* or -1 if value is not found
*
* @param value value sought.
* @return index (0 is first element) of value, or -1
*/
public int indexOf(ELTTYPE value)
{
int i = 0;
SinglyLinkedListElement finger = tail.next();
// search for value or end of list, counting along way
while (finger != null && !finger.value().equals(value))
{
if (finger == tail) { // we fall off list
finger = null;
} else {
finger = finger.next();
}
i++;
}
// finger points to value, i is index
if (finger == null)
{ // value not found, return indicator
return -1;
} else {
// value found, return index
return i;
}
}
/**
* Determine last location of a value in list.
*
* @pre value is not null
* @post returns (0-origin) index of value,
* or -1 if value is not found
*
* @param value value sought.
* @return index (0 is first element) of value, or -1
*/
public int lastIndexOf(ELTTYPE value)
{
int result = -1; // assume not found, return -1
int i = 0;
SinglyLinkedListElement finger = tail.next();
// search for last matching value, result is desired index
while (finger != null)
{
// a match? keep track of location
if (finger.value().equals(value)) result = i;
if (finger == tail) {
finger = null;
} else {
finger = finger.next();
}
i++;
}
// return last match
return result;
}
//+interface
/**
* Construct an iterator over elements of list.
* Elements are traversed from head to tail.
*
* @post returns iterator to traverse list elements
*
* @return iterator associated with list.
*/
public Iterator iterator()
//-interface
{
return new CircularListIterator(tail);
}
//+interface
/**
* Determine if a list is empty.
*
* @post returns true if no elements in list
*
* @return True iff list is empty.
*/
public boolean isEmpty()
//-interface
{
return tail == null;
}
//+interface
/**
* Remove elements of list.
*
* @post removes all elements from list
*/
public void clear()
//-interface
{
count = 0;
tail = null;
}
/**
* Generate a string representation of list.
*
* @post returns a string representing list
*
* @return String representing list.
*/
public String toString()
{
StringBuffer s = new StringBuffer();
s.append("");
return s.toString();
}
//+interface
}
//-interface