Lists

The Basics

A list is a completely unrestricted sequence. You can add, update, or remove items at any position. You can lookup an item by position, or find the index at which a given value appears.

There are tons of list operations. Here is a small sampling:

Exercise: Give ten more.

Representations

Exercise: Why might singly linked notes be less than desirable? After all, stacks and queues can use singly-linked structures just fine.
Exercise: What's with this header? Does it take up a lot of extra space? What is the alternative to using a header, and what are its disadvantages?

Lists in Java

There's already a List interface in the Java Core API and a whole bunch of implementing subclasses, but we're going to write our own list class, because

A Small, Doubly-Linked Implementation

package edu.lmu.cs.rtoal.collections;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * A simple linked list class, implemented completely from scratch,
 * using doubly linked nodes and a cool "header" node to greatly
 * simplify insertion and deletions on the end of the list.
 */
public class SimpleLinkedList {

    /**
     * Doubly-linked node class, completely private to the List class,
     * as clients don't care about the implementation of the list.
     */
    private class Node {
        Object item;
        Node next;
        Node previous;
        Node(Object item, Node next, Node previous) {
            this.item = item;
            this.next = next;
            this.previous = previous;
        }
    }

    /**
     * The list itself maintains only a reference to its "header" node.
     * The header is a node that does not store any data.  Its 'next'
     * field points to the first item in the list and its 'previous'
     * field points to the last item.    This makes all insertions and
     * deletions uniform, even at the beginning and the end of the list!
     */
    private Node header = new Node(null, null, null);

    /**
     * The number of items in the list, stored to make size() O(1).
     */
    private int size = 0;

    /**
     * The number of modifications to the list, tracked to ensure
     * that all iterators are fail-safe.
     */
    private long changes = 0;

    /**
     * Constructs a new empty list.
     */
    public SimpleLinkedList() {
        header = new Node(null, null, null);
        header.next = header.previous = header;
    }

    /**
     * Returns the number of items in the list.
     */
    public int size() {
        return size;
    }

    /**
     * Inserts <code>item</code> as the new first item.
     */
    public void addFirst(Object item) {
        addBefore(item, header.next);
    }

    /**
     * Inserts <code>item</code> as the new last item.
     */
    public void addLast(Object item) {
        addBefore(item, header);
    }

    /**
     * Inserts <code>item</code> so that after insertion it is the
     * item at the given index position.
     *
     *  @throws IndexOutOfBoundsException if index not in [0,size].
     */
    public void add(int index, Object item) {
        addBefore(item, (index == size ? header : nodeAt(index)));
    }

    /**
     * Removes the item at the given index position.
     *
     *  @throws IndexOutOfBoundsException if index not in [0,size).
     */
    public void remove(int index) {
        remove(nodeAt(index));
    }

    /**
     * Returns the item at the given index position.
     *
     *  @throws IndexOutOfBoundsException if index not in [0,size).
     */
    public Object get(int index) {
        return nodeAt(index).item;
    }

    /**
     * Replaces the item at the given index position with
     * <code>item</code>.
     *
     *  @throws IndexOutOfBoundsException if index not in [0,size).
     */
    public void set(int index, Object item) {
        nodeAt(index).item = item;
    }

    /**
     * Returns the index of the first item ".equals" to the given item.
     */
    public int indexOf(Object item) {
        int index = 0;
        for (Node node = header.next; node != header; node = node.next) {
            if (node.item.equals(item)) {
                return index;
            }
            index++;
        }
        return -1;
    }

    /**
     * Returns a non-removing iterator over this list.
     */
    public Iterator iterator() {
        return new SimpleListIterator();
    }

    private class SimpleListIterator implements Iterator {
        private Node current = header;
        private long changesAtConstructionTime = changes;

        public boolean hasNext() {
            return current.next != header;
        }

        public Object next() {
            if (changes != changesAtConstructionTime) {
                throw new ConcurrentModificationException();
            }
            if (current.next == header) {
                 throw new NoSuchElementException();
            }
            current = current.next;
            return current.item;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    //
    // PRIVATE HELPER METHODS
    //

    private Node nodeAt(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException(index + " for size " + size);
        }
        Node n = header;
        for (int i = 0; i <= index; i++) {
            n = n.next;
        }
        return n;
    }

    private void addBefore(Object o, Node n) {
        Node newNode = new Node(o, n, n.previous);
        newNode.previous.next = newNode;
        newNode.next.previous = newNode;
        size++;
        changes++;
    }

    private void remove(Node n) {
        if (n == null || n == header) {
            throw new NoSuchElementException();
        }
        n.previous.next = n.next;
        n.next.previous = n.previous;
        size--;
        changes++;
    }
}

Class Diagram

Exercise: Give the class diagram for our SimpleLinkedList class.

Unit Testing

This class is pretty large; we expect the test suite to be huge. Here is a partial list what we need to test:

Exercise: This tester is for you to write.
Exercise: Hmmm. We wrote a class without making an interface. If you're using Eclipse, try out the "Extract Interface" feature. Otherwise make an interface for this class by hand.