/** * A list implemented using a doubly-linked circular list * @author Edo Biagioni * @lecture ICS 211 Feb 1 (or later) * @date January 27, 2011 */ public class DLinkedList<E> { // here, include the DLinkedNode definition /** * A node in a doubly-linked list * @author Edo Biagioni * @lecture ICS 211 Feb 1 * @date January 27, 2010 * @bugs private class: include this code within a larger class */ private static class DLinkedNode<E> { private E item; private DLinkedNode<E> prev; private DLinkedNode<E> next; /** * constructor to build a node with no successor * @param the value to be stored by this node */ private DLinkedNode(E value) { item = value; next = null; prev = null; } /** * constructor to build a node with a specified (perhaps null) successor * @param the value to be stored by this node * @param the prev field for this node * @param the next field for this node */ private DLinkedNode(E value, DLinkedNode<E> prev, DLinkedNode<E> next) { item = value; this.next = next; this.prev = prev; } } // this is the start of the linked list. If the list is empty, it is null protected DLinkedNode<E> head; // since this is a circular double-linked list, the tail node // can always be found at head.prev, so we don't explicitly store it protected int size; // there are some relationships between the class variables. This // method checks that these relationships always hold. Any // property that always holds is called an invariant. // the property may not hold in the middle of a method, // so only call this at the beginning or end of a public method. /** * checks assertion * @throws java.lang.AssertionError if the assertion is not true */ private void verify(boolean mustBeTrue) { if (! mustBeTrue) { throw new java.lang.AssertionError("assertion error"); } } /** * checks class invariants * @throws java.lang.AssertionError if the invariant is violated */ private void checkInvariants() { // uncomment the next line to skip the checks: // return; // either head and tail are both null, or neither is null. // size is zero if and only if they are null, and otherwise is positive verify((size == 0) == (head == null)); verify(size >= 0); if (size == 0) { return; // no more checks } // check to make sure size is the same as the length of the list. // this code takes O(n), so comment it out if performance is important int measuredSize = 0; DLinkedNode<E> node = head; do { node = node.next; measuredSize++; } while (node != head); verify(measuredSize == size); } /** * initializes an empty linked list */ public DLinkedList() { head = null; size = 0; // one of the constructor's jobs is to make sure that the invariants hold. checkInvariants(); } /** * adds a value to the end of the list * @param the value to be added * @return true (the add always succeeds) */ public boolean add(E value) { checkInvariants(); DLinkedNode<E> newNode = new DLinkedNode<E>(value); if (head == null) { head = newNode; newNode.next = head; newNode.prev = head; } else { DLinkedNode<E> tail = head.prev; tail.next = newNode; head.prev = newNode; newNode.next = head; newNode.prev = tail; } size++; checkInvariants(); return true; } /** * returns the node at the requested position, may take time O(n) * @param the position of the requested node, 0 for the head node * @return the requested node * @throws NullPointerException if the index is larger than the linked list */ private DLinkedNode<E> nodeAtPosition(int index) { verify (index >= 0); DLinkedNode<E> result = head; while (index > 0) { result = result.next; index--; } verify (result != null); return result; } /** * removes a value from the given position in the list * @param the position at which to add: 0 to add at the start * @throws IndexOutOfBoundsException if the index is less than 0 * or greater than or equal to the number of elements in * the linked list */ public E remove(int index) { checkInvariants(); if ((index < 0) || (index >= size)) { String badIndex = new String ("index " + index + " must be between 0 and " + (size - 1)); throw new IndexOutOfBoundsException(badIndex); } // if head is null, by the invariants, either index < 0 or index >= size // therefore head cannot be null // just to be sure, let the program verify that verify (head != null); E value = head.item; if (size == 1) { head = null; // very simple!! } else { DLinkedNode<E> node = nodeAtPosition(index); value = node.item; // return the value if (index == 0) { // removing the head node head = node.next; // new head node == old second node } node.prev.next = node.next; // get this node out of the list node.next.prev = node.prev; } size--; checkInvariants(); return value; } /** * concatenates the elements of the linked list, separated by " ==> " * @return the string representation of the list */ public String toString() { checkInvariants(); DLinkedNode<E> node = head; StringBuffer result = new StringBuffer(); if (head != null) { while (true) { result.append (node.item.toString()); node = node.next; if (node == head) { break; // entire list has been traversed } result.append (" ==> "); } } checkInvariants(); // make sure we didn't break anything return result.toString(); } /** * unit test method -- basic testing of the functionality * @param required, ignored */ public static void main (String [] arguments) { DLinkedList<String> dll = new DLinkedList<String>(); System.out.println (dll); dll.add ("hello"); System.out.println (dll); dll.add ("world"); System.out.println (dll); dll.add ("foo"); System.out.println (dll); dll.add ("bar"); System.out.println (dll); dll.add ("baz"); System.out.println (dll); dll.remove (2); // remove foo System.out.println (dll); dll.remove (3); // remove baz System.out.println (dll); dll.remove (1); // remove world System.out.println (dll); dll.remove (0); // remove hello System.out.println (dll); dll.remove (0); // remove hello System.out.println (dll); } }