/** * SentinelDLL.java * * Implementation of circular, doubly linked list with a sentinel * WARNING: This implementation is guaranteed to work only if always given * objects that are immutable (or at least do not change). * Handling errors by throwing exceptions would be a better way of * dealing with errors, but this is a demo program. * * @author THC * @author Scot Drysdale converted it to Java. * @author THC and Scot Drysdale have made a number of modifications. * @author Scot Drysdale most recently modified on 1/12/2011 */ public class SentinelDLL implements CS10LinkedList { private Element sentinel; // sentinel, serves as head and tail private Element current; // current position in the list /** * A private inner class representing the elements in the list. */ private static class Element { // Because this is a private inner class, these can be seen from SentinelDLL, // but not from outside SentinelDLL. private T data; // reference to data stored in this element private Element next; // reference to next item in list private Element previous; // reference to previous item in list /** * Constructor for a linked list element, given an object. * @param obj the data in the element. */ public Element(T obj) { next = previous = null; // no element before or after this one, yet data = obj; // OK to copy reference, since obj references an immutable object } /** * @return the String representation of a linked list element. */ public String toString() { return data.toString(); } } /** * Constructor for a circular, doubly linked list with a sentinel. * Makes an empty list. */ public SentinelDLL() { sentinel = new Element(null); clear(); } /* * @see CS10LinkedList#clear() */ public void clear() { // Make the list be empty by having the sentinel point to itself // in both directions. sentinel.next = sentinel.previous = sentinel; // There's only one place to put the current reference... current = sentinel; } /** * @see CS10LinkedList#add() */ public void add(T obj) { Element x = new Element(obj); // allocate a new element // Splice in the new element. x.next = current.next; x.previous = current; current.next.previous = x; current.next = x; current = x; // new element is current position } /** * * @see CS10LinkedList#remove() */ public void remove() { // Do not ever let the sentinel be deleted! if (hasCurrent()) { // Splice out the current element. current.previous.next = current.next; current.next.previous = current.previous; current = current.next; // make successor the new current } else System.err.println("No current item"); } /** * @return the String representation of this list. */ public String toString() { String result = ""; for (Element x = sentinel.next; x != sentinel; x = x.next) result += x.toString() + "\n"; return result; } /** * @see CS10LinkedList#contains() */ public boolean contains(T obj) { Element x; // Since we have a sentinel that isn't being used for a "real" element, // we put the object we are looking for in the sentinel. That way, we // know we'll find it some time during the search. sentinel.data = obj; for (x = sentinel.next; !x.data.equals(obj); x = x.next) ; // We dropped out of the loop because we found the target object in an element // that x references. // If we found it in the sentinel, it wasn't really in the list. // If we found it before getting back to the sentinel, it was in the list. // Put the sentinel back into its null state. sentinel.data = null; if (x == sentinel) return false; // tell the caller that we did not find the object else { current = x; // remember where we found it return true; // and tell the caller } } /** * @see CS10LinkedList#isEmpty() */ public boolean isEmpty() { return sentinel.next == sentinel; } /** * @see CS10LinkedList#hasCurrent() */ public boolean hasCurrent() { return current != sentinel; } /** * @see CS10LinkedList#hasNext() */ public boolean hasNext() { return hasCurrent() && current.next != sentinel; } /** * @see CS10LinkedList#getFirst() */ public T getFirst() { if(isEmpty()) { System.err.println("The list is empty"); return null; } current = sentinel.next; return get(); } /** * @see CS10LinkedList#getLast() */ public T getLast() { if(isEmpty()) { System.err.println("The list is empty"); return null; } current = sentinel.previous; return get(); } /** * @see CS10LinkedList#addFirst() */ public void addFirst(T obj) { current = sentinel; add(obj); } /** * @see CS10LinkedList#addLast() */ public void addLast(T obj) { current = sentinel.previous; add(obj); } /** * @see CS10LinkedList#get() */ public T get() { if (hasCurrent()) return current.data; else { System.err.println("No current item"); return null; } } /** * @see CS10LinkedList#set() */ // Error if there is no current item. public void set(T obj) { if (hasCurrent()) current.data = obj; else System.err.println("No current item"); } /** * @see CS10LinkedList#next() */ // Moves current to the next position. Error if there is no next item. public T next() { if (hasNext()) { current = current.next; return current.data; } else { System.err.println("No next item"); return null; } } }