Maciej Rumianowski's Weblog 

OpenJDK java.util.LinkedList

by Maciej Rumianowski


Posted on Thursday Oct 17, 2013 at 02:42PM in Code in Practice


Doubly linked list in java is implemented by java.util.LinkedList. This class extends AbstractSequentialList and implements interfaces: List, Deque, Cloneable, Serializable (Iterable, Collection, Queue). Javadocs can be found on docs.oracle.com and code viewer on grepcode.com.

LinkedList compared to ArrayList has faster insertion time at the end and beginning of List, but slower access time. LinkedList doesn't implement RandomAccess interface, because while getting elements by index it has to iterate through list from beginning or end. Below are shown methods for accessing and inserting elements.

     /**
     * Returns the element at the specified position in this list.
     *
     * @param index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        return entry(index).element;
    }

    /**
     * Returns the indexed entry.
     */
    private Entry<E> entry(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+size);
        Entry<E> e = header;
        if (index < (size >> 1)) {
            for (int i = 0; i <= index; i++)
                e = e.next;
        } else {
            for (int i = size; i > index; i--)
                e = e.previous;
        }
        return e;
    }

As you can see based on in which half of the list is index entry method iterates from beginning or end of the list.

   /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        addBefore(e, header);
        return true;
    }

    private Entry<E> addBefore(E e, Entry<E> entry) {
        Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
        newEntry.previous.next = newEntry;
        newEntry.next.previous = newEntry;
        size++;
        modCount++;
        return newEntry;
    }

ConcurrentModificationException is another interesting topic related to LinkedList and all classes implementing Iterable interface with fail-fast concept. After creation of Iterator instance for given List (iterator or listIterator methods) any modifications list using, other methods than provided by Iterator, will cause ConcurrentModificationException. Below is implementation of ListIterator returned by LinkedList.listIterator().

    private class ListItr implements ListIterator<E> {
        private Entry<E> lastReturned = header;
        private Entry<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;

        ListItr(int index) {
            if (index < 0 || index > size)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+size);
            if (index < (size >> 1)) {
                next = header.next;
                for (nextIndex=0; nextIndex<index; nextIndex++)
                    next = next.next;
            } else {
                next = header;
                for (nextIndex=size; nextIndex>index; nextIndex--)
                    next = next.previous;
            }
        }

        public boolean hasNext() {
            return nextIndex != size;
        }

        public E next() {
            checkForComodification();
            if (nextIndex == size)
                throw new NoSuchElementException();

            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.element;
        }

        public boolean hasPrevious() {
            return nextIndex != 0;
        }

        public E previous() {
            if (nextIndex == 0)
                throw new NoSuchElementException();

            lastReturned = next = next.previous;
            nextIndex--;
            checkForComodification();
            return lastReturned.element;
        }

        public int nextIndex() {
            return nextIndex;
        }

        public int previousIndex() {
            return nextIndex-1;
        }

        public void remove() {
            checkForComodification();
            Entry<E> lastNext = lastReturned.next;
            try {
                LinkedList.this.remove(lastReturned);
            } catch (NoSuchElementException e) {
                throw new IllegalStateException();
            }
            if (next==lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = header;
            expectedModCount++;
        }

        public void set(E e) {
            if (lastReturned == header)
                throw new IllegalStateException();
            checkForComodification();
            lastReturned.element = e;
        }

        public void add(E e) {
            checkForComodification();
            lastReturned = header;
            addBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

As you can see most of methods calls checkForComodification() which verifies that modifications to List were made only through ListItr instance. ListItr is a private inner class and has access to methods and variables of outer class such as modCount and addBefore(). Variable expectedModCount is initialised with modCount on instantiation of ListItr and on every call of ListItr methods both variables are checked to be equal.



No one has commented yet.

Leave a Comment

HTML Syntax: NOT allowed