doubly linked positional list

  • Design an abstract data type that provides a user a way to refer to elements anywhere in a sequence, and to perform arbitrary constant time insertions and deletions.
  • A position p associated with an element e does not change, even if the index of e changes due to insertions or deletions.
  • A position p does not change if we replace the element e stored at p with another element.
  • The only way in which a position becomes invalid is if that position (and its element) are explicitly removed from the list.
  • The way to identify locations within a linked list are node references. Nodes are positions.
public interface Position<E> {
    E getElement() throws IllegalStateException;
}
public interface PositionalList<E> {
    int size();
    boolean isEmpty();
    Position<E> first();
    Position<E> last();
    Position<E> before(Position<E> p) throws IllegalArgumentException;
    Position<E> after(Position<E> p) throws IllegalArgumentException;
    Position<E> addFirst(E e);
    Position<E> addLast(E e);
    Position<E> addBefore(Position<E> p, E e) throws IllegalArgumentException;
    Position<E> addAfter(Position<E> p, E e) throws IllegalArgumentException;
    E set(Position<E> p, E e) throws IllegalArgumentException;
    E remove(Position<E> p) throws IllegalArgumentException;
    Iterable<Position<E>> positions();
}
public class LinkedPositionalList<E> implements PositionalList<E>, Iterable<E> {
    private static class Node<E> implements Position<E> {
        private E element;
        private Node<E> next;
        private Node<E> prev;

        public Node(E e, Node<E> prevNode, Node<E> nextNode) {
            element = e;
            prev = prevNode;
            next = nextNode;
        }
        public Node<E> getNext() { return next; }
        public Node<E> getPrev() { return prev; }
        public void setNext(Node<E> nextNode) { next = nextNode; }
        public void setPrev(Node<E> prevNode) { prev = prevNode; }
        public void setElement(E e) { element = e; }
        @Override
        public E getElement() throws IllegalStateException {
            if (next == null) throw new IllegalStateException("Position no longer valid");
            return element;
        }
    }

    private Node<E> headSentinel;
    private Node<E> tailSentinel;
    private int size;

    public LinkedPositionalList() {
        headSentinel = new Node<>(null, null, null);
        tailSentinel = new Node<>(null, headSentinel, null);
        headSentinel.setNext(tailSentinel);
    }
    @Override
    public int size() { return size; }
    @Override
    public boolean isEmpty() { return (size == 0); }
    @Override
    public Position<E> first() {
        if (isEmpty()) return null;
        return headSentinel.getNext();
    }
    @Override
    public Position<E> last() {
        if (isEmpty()) return null;
        return tailSentinel.getPrev();
    }
    @Override
    public Position<E> before(Position<E> p) throws IllegalArgumentException {
        if (!(p instanceof Node)) throw new IllegalArgumentException("Invalid p");
        if (((Node<E>) p).getNext() == null) throw new IllegalArgumentException("Position no longer valid");
        return ((Node<E>) p).getPrev();
    }
    @Override
    public Position<E> after(Position<E> p) throws IllegalArgumentException {
        if (!(p instanceof Node)) throw new IllegalArgumentException("Invalid p");
        if (((Node<E>) p).getNext() == null) throw new IllegalArgumentException("Position no longer valid");
        return ((Node<E>) p).getNext();
    }
    @Override
    public Position<E> addFirst(E e) {
        Node<E> addNode = new Node<>(e, headSentinel,headSentinel.getNext());
        headSentinel.getNext().setPrev(addNode);
        headSentinel.setNext(addNode);
        size++;
        return addNode;
    }
    @Override
    public Position<E> addLast(E e) {
        Node<E> addNode = new Node<>(e, tailSentinel.getPrev(),tailSentinel);
        tailSentinel.getPrev().setNext(addNode);
        tailSentinel.setPrev(addNode);
        size++;
        return addNode;
    }
    @Override
    public Position<E> addBefore(Position<E> p, E e) throws IllegalArgumentException {
        if (!(p instanceof Node)) throw new IllegalArgumentException("Invalid p");
        if (((Node<E>) p).getNext() == null) throw new IllegalArgumentException("Position no longer valid");
        Node<E> temp = (Node<E>) p;
        Node<E> addNode = new Node<>(e, temp.getPrev(), temp);
        temp.getPrev().setNext(addNode);
        temp.setPrev(addNode);
        size++;
        return addNode;
    }

    @Override
    public Position<E> addAfter(Position<E> p, E e) throws IllegalArgumentException {
        if (!(p instanceof Node)) throw new IllegalArgumentException("Invalid p");
        if (((Node<E>) p).getNext() == null) throw new IllegalArgumentException("Position no longer valid");
        Node<E> temp = (Node<E>) p;
        Node<E> addNode = new Node<>(e, temp, temp.getNext());
        temp.getNext().setPrev(addNode);
        temp.setNext(addNode);
        size++;
        return addNode;
    }

    @Override
    public E set(Position<E> p, E e) throws IllegalArgumentException {
        if (!(p instanceof Node)) throw new IllegalArgumentException("Invalid p");
        if (((Node<E>) p).getNext() == null) throw new IllegalArgumentException("Position no longer valid");
        Node<E> temp = (Node<E>) p;
        temp.setElement(e);
        return temp.getElement();
    }

    @Override
    public E remove(Position<E> p) throws IllegalArgumentException {
        if (!(p instanceof Node)) throw new IllegalArgumentException("Invalid p");
        if (((Node<E>) p).getNext() == null) throw new IllegalArgumentException("Position no longer valid");
        Node<E> temp = (Node<E>) p;
        temp.getPrev().setNext(temp.getNext());
        temp.getNext().setPrev(temp.getPrev());
        size--;
        E tempElement = temp.getElement();
        temp.setPrev(null);
        temp.setNext(null);
        temp.setElement(null);
        return tempElement;
    }
    
    @Override
    public Iterable<Position<E>> positions() {
        java.util.List<Position<E>> snapshot = new java.util.ArrayList<>();
        Position<E> current = first();
        while (current != null) {
            snapshot.add(current);
            current = after(current);
        }
        return snapshot;
    }

    private class ElementIterator implements Iterator<E> {
        private Position<E> cursor = first();
        private Position<E> recent = null;

        @Override
        public boolean hasNext() { return (cursor != null); }
        @Override
        public E next() throws NoSuchElementException {
            if (cursor == null) throw new NoSuchElementException();
            E answer = cursor.getElement();
            recent = cursor;
            cursor = after(recent);
            return answer;
        }

        @Override
        public void remove() throws NoSuchElementException{
            if (recent == null) throw new NoSuchElementException();
            LinkedPositionalList.this.remove(recent);
            recent = null;
        }
    }
    @Override
    public Iterator<E> iterator() {
        return new ElementIterator();
    }
}