1. 程式人生 > >Data Structure - Doubly Linked List (Java)

Data Structure - Doubly Linked List (Java)

package chimomo.learning.java.datastructure;

/**
 * @author Created by Chimomo
 */
public class DoublyLinkedList<AnyType> implements Iterable<AnyType> {

    private Node<AnyType> head;
    private Node<AnyType> tail;
    private int size;
    private int modificationCount = 0;

    /**
     * Construct an empty DoublyLinkedList.
     */
    public DoublyLinkedList() {
        clear();
    }

    /**
     * Change the size of this collection to zero.
     */
    private void clear() {
        head = new Node<>(null, null, null);
        tail = new Node<>(null, head, null);
        head.next = tail;

        size = 0;
        modificationCount++;
    }

    /**
     * Returns the number of items in this collection.
     *
     * @return The number of items in this collection.
     */
    public int size() {
        return size;
    }

    /**
     * Is doubly linked list empty or not.
     *
     * @return True if empty, false otherwise.
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * Adds an item to this collection, at the end.
     *
     * @param x Any object.
     * @return True.
     */
    public boolean add(AnyType x) {
        add(size(), x);
        return true;
    }

    /**
     * Adds an item to this collection, at specified position.
     * Items at or after that position are slid one position higher.
     *
     * @param x   Any object.
     * @param idx Position to add at.
     * @throws IndexOutOfBoundsException If idx is not between 0 and size(), inclusive.
     */
    public void add(int idx, AnyType x) {
        addBefore(getNode(idx, 0, size()), x);
    }

    /**
     * Adds an item to this collection, at specified position p.
     * Items at or after that position are slid one position higher.
     *
     * @param p Node to add before.
     * @param x Any object.
     * @throws IndexOutOfBoundsException If idx is not between 0 and size(), inclusive.
     */
    private void addBefore(Node<AnyType> p, AnyType x) {
        Node<AnyType> newNode = new Node<>(x, p.prev, p);
        newNode.prev.next = newNode;
        p.prev = newNode;
        size++;
        modificationCount++;
    }

    /**
     * Returns the item at position idx.
     *
     * @param idx The index to search in.
     * @throws IndexOutOfBoundsException If index is out of range.
     */
    public AnyType get(int idx) {
        return getNode(idx).data;
    }

    /**
     * Sets the item at position idx.
     *
     * @param idx    The index to change.
     * @param newVal The new value.
     * @return The old value.
     * @throws IndexOutOfBoundsException If index is out of range.
     */
    public AnyType set(int idx, AnyType newVal) {
        Node<AnyType> p = getNode(idx);
        AnyType oldVal = p.data;

        p.data = newVal;
        return oldVal;
    }

    /**
     * Gets the node at position idx, which must range from 0 to size() - 1.
     *
     * @param idx Index to search at.
     * @return Internal node corresponding to idx.
     * @throws IndexOutOfBoundsException If idx is not between 0 and size() - 1, inclusive.
     */
    private Node<AnyType> getNode(int idx) {
        return getNode(idx, 0, size() - 1);
    }

    /**
     * Gets the node at position idx, which must range from lower to upper.
     *
     * @param idx   Index to search at.
     * @param lower Lowest valid index.
     * @param upper Highest valid index.
     * @return Internal node corresponding to idx.
     * @throws IndexOutOfBoundsException If idx is not between lower and upper, inclusive.
     */
    private Node<AnyType> getNode(int idx, int lower, int upper) {
        Node<AnyType> p;

        if (idx < lower || idx > upper)
            throw new IndexOutOfBoundsException("Get node index: " + idx + "; size: " + size());

        if (idx < size() / 2) {
            p = head.next;
            for (int i = 0; i < idx; i++)
                p = p.next;
        } else {
            p = tail;
            for (int i = size(); i > idx; i--)
                p = p.prev;
        }

        return p;
    }

    /**
     * Removes an item from this collection.
     *
     * @param idx The index of the object.
     * @return The item was removed from the collection.
     */
    public AnyType remove(int idx) {
        return remove(getNode(idx));
    }

    /**
     * Removes the object contained in Node p.
     *
     * @param p The node containing the object.
     * @return The item was removed from the collection.
     */
    private AnyType remove(Node<AnyType> p) {
        p.next.prev = p.prev;
        p.prev.next = p.next;
        size--;
        modificationCount++;

        return p.data;
    }

    /**
     * Returns a string representation of this collection.
     */
    public String toString() {
        StringBuilder sb = new StringBuilder("[ ");

        for (AnyType x : this) {
            sb.append(x).append(" ");
        }
        sb.append("]");

        return new String(sb);
    }

    /**
     * Obtains an iterator object used to traverse the collection.
     *
     * @return An iterator positioned prior to the first element.
     */
    public java.util.Iterator<AnyType> iterator() {
        return new LinkedListIterator();
    }

    /**
     * This is the doubly-linked list node.
     */
    private static class Node<AnyType> {

        private AnyType data;
        private Node<AnyType> prev;
        private Node<AnyType> next;

        Node(AnyType data, Node<AnyType> prev, Node<AnyType> next) {
            this.data = data;
            this.prev = prev;
            this.next = next;
        }

    }

    /**
     * This is the implementation of the LinkedListIterator.
     * It maintains a notion of a current position and of course the implicit reference to the DoublyLinkedList.
     */
    private class LinkedListIterator implements java.util.Iterator<AnyType> {

        private Node<AnyType> current = head.next;
        private int expectedModificationCount = modificationCount;
        private boolean okToRemove = false;

        public boolean hasNext() {
            return current != tail;
        }

        public AnyType next() {
            if (modificationCount != expectedModificationCount) {
                throw new java.util.ConcurrentModificationException();
            }
            if (!hasNext()) {
                throw new java.util.NoSuchElementException();
            }

            AnyType nextItem = current.data;
            current = current.next;
            okToRemove = true;
            return nextItem;
        }

        public void remove() {
            if (modificationCount != expectedModificationCount) {
                throw new java.util.ConcurrentModificationException();
            }
            if (!okToRemove) {
                throw new IllegalStateException();
            }

            DoublyLinkedList.this.remove(current.prev);
            expectedModificationCount++;
            okToRemove = false;
        }

    }

    public static void main(String[] args) {
        // Construct a doubly linked list.
        DoublyLinkedList<Integer> lst = new DoublyLinkedList<>();

        // Is empty?
        System.out.println("Is empty? " + lst.isEmpty());

        // Insert.
        for (int i = 0; i < 10; i++) {
            lst.add(i);
        }
        for (int i = 20; i < 30; i++) {
            lst.add(0, i);
        }
        System.out.println("Is empty? " + lst.isEmpty());

        // Remove.
        lst.remove(0);
        lst.remove(lst.size() - 1);
        System.out.println(lst);

        // Iterate.
        java.util.Iterator<Integer> itr = lst.iterator();
        while (itr.hasNext()) {
            itr.next();
            itr.remove();
            System.out.println(lst);
        }
    }

}

/*
Output:
Is empty? true
Is empty? false
[ 28 27 26 25 24 23 22 21 20 0 1 2 3 4 5 6 7 8 ]
[ 27 26 25 24 23 22 21 20 0 1 2 3 4 5 6 7 8 ]
[ 26 25 24 23 22 21 20 0 1 2 3 4 5 6 7 8 ]
[ 25 24 23 22 21 20 0 1 2 3 4 5 6 7 8 ]
[ 24 23 22 21 20 0 1 2 3 4 5 6 7 8 ]
[ 23 22 21 20 0 1 2 3 4 5 6 7 8 ]
[ 22 21 20 0 1 2 3 4 5 6 7 8 ]
[ 21 20 0 1 2 3 4 5 6 7 8 ]
[ 20 0 1 2 3 4 5 6 7 8 ]
[ 0 1 2 3 4 5 6 7 8 ]
[ 1 2 3 4 5 6 7 8 ]
[ 2 3 4 5 6 7 8 ]
[ 3 4 5 6 7 8 ]
[ 4 5 6 7 8 ]
[ 5 6 7 8 ]
[ 6 7 8 ]
[ 7 8 ]
[ 8 ]
[ ]

*/