views:

235

answers:

5

I have a private LinkedList in a Java class & will frequently need to retrieve the last element in the list. The lists need to scale, so I'm trying to decide whether I need to keep a reference to the last element when I make changes (to achieve O(1)) or if the LinkedList class does that already with the getLast() call.

What is the big-O cost of LinkedList.getLast() and is it documented? (i.e. can I rely on this answer or should I make no assumptions & cache it even if it's O(1)?)

+4  A: 

It is O(1) because the list is doubly-linked. It keeps references to both head and tail.

From documentation:

Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.

doublep
It's not only doubly linked, it's also cyclic.
Helper Method
+1 for citing spec
Kevin Bourrillion
+4  A: 

It is O(1) and you should not have to cache it. The getLast method simply returns header.previous.element, so there is no computation and no traversal of the list. A linked list slows down when you need to find elements in the middle it since it starts one end and moves one element at a time.

Jeff Storey
+1  A: 

From the LinkedList docs:

All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.

It should be O(1) since a doubly-linked list will have a reference to its own tail. (Even if it doesn't explicitly keep a reference to its tail, it will be O(1) to find its tail.)

Bill the Lizard
A: 

The implementation of LinkedList.getLast() leaves no doubts - it's an O(1) operation. However, I didn't find it documented anywhere.

Eyal Schneider
A: 

From the Java 6 source code:

* @author  Josh Bloch
 * @version 1.67, 04/21/06
 * @see     List
 * @see     ArrayList
 * @see     Vector
 * @since 1.2
 * @param <E> the type of elements held in this collection
 */

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    private transient Entry<E> header = new Entry<E>(null, null, null);
    private transient int size = 0;

    /**
     * Constructs an empty list.
     */
    public LinkedList() {
        header.next = header.previous = header;
    }

...

    /**
     * Returns the first element in this list.
     *
     * @return the first element in this list
     * @throws NoSuchElementException if this list is empty
     */
    public E getFirst() {
    if (size==0)
        throw new NoSuchElementException();

    return header.next.element;
    }

    /**
     * Returns the last element in this list.
     *
     * @return the last element in this list
     * @throws NoSuchElementException if this list is empty
     */
    public E getLast()  {
    if (size==0)
        throw new NoSuchElementException();

    return header.previous.element;
    }

...

}

so that's O(1) for both getFirst() & getLast()

Yaneeve