ArrayList vs LinkedList

1 List Interface

  • The ArrayList and LinkedList both implement the List interface below.
  • The difference not that some operations are only possible in one data structure—they both can support them all
  • The key is that for each data structure, some operations are efficient, while others are not
  • This is what you will analyze in this activity
public interface List {
    // pre: 0 <= i < size()
    // post: returns object found at that location
    public E get(int i);

    // pre: list is not empty
    // post: returns first value in list
    public E getFirst();

    // pre: list is not empty
    // post: returns last value in list
    public E getLast();

    // pre: 0 <= i < size()
    // post: set the value stored at i to value, returning the old value.
    public void set(int i, E value);

    // post: value is added to beginning of list
    public void addFirst(E value);

    // post: value is added to end of list
    public void addLast(E value);

    // pre: list is not empty
    // post: removes first value from list
    public E removeFirst();

    // pre: list is not empty
    // post: removes last value from list
    public E removeLast();

    // pre: value is not null
    // post: first element matching value is removed from list
    public E remove(E value);

    // pre: value is not null
    // post: returns true if value is in the list
    public boolean contains(E value)
}

2 Comparing operations

To analyze the efficiency of a method, we count how many steps each method had to do (where one line of Java code typically is one step). In particular, we are looking at the worst case—what the highest possible number of steps could be. If the number of steps would be the same no matter how big the list is (i.e., no matter how many elements have been added to the ArrayList or LinkedList), we say the efficiency is constant. If the number of steps is proportional to the number of elements, we say the efficiency is linear.

2.1 Constructors and fields

For reference, here are the fields and constructors for both data structures:

public class ArrayList<E> {                   public class DoublyLinkedList<E> {
    private E[] elementData;                       
    private int elementCount;                     private class ListNode {
                                                      private E value;        // data value at node
    public ArrayList() {                              private ListNode prev;  // reference to previous node
        elementData = (E[]) new Object[10];           private ListNode next;  // reference to next node
        elementCount = 0;                        
    }                                                 // construct a ListNode and adjust neighboring next and prev
                                                      public ListNode(E val, ListNode before, ListNode after) {
                                                          value = val;
                                                          prev = before;
                                                          next = after;

                                                          if (before != null) {
                                                              before.next = this;
                                                          }
                                                          if (after != null) {
                                                              after.prev = this;
                                                          }
                                                      }
                                                  }

                                                  private int count;
                                                  private ListNode head;
                                                  private ListNode tail;

                                                  public DoublyLinkedList() {
                                                      head = null;
                                                      tail = null;
                                                      count = 0;
                                                  }

2.2 ensureCapacity

For reference, here is the ArrayList's ensureCapacity method:

// post: the capacity of this array is at least minCapacity
public void ensureCapacity(int minCapacity) {
    // only need to do something if we don't already have the capacity
    if (elementData.length < minCapacity) {
        int newLength = elementData.length; // initial guess
        // double the size of our expanded array until it's big enough for minCapacity
        while (newLength < minCapacity) {
            newLength *= 2;
        }

        // guaranteed: newLength > elementData.length.
        E[] newElementData = (E[]) new Object[newLength];

        // copy old data to new array
        for (int i = 0; i < elementCount; i++) {
            newElementData[i] = elementData[i];
        }
        elementData = newElementData; // reassign elementData to refer to the new, larger array
    }
}

2.3 get

public E get(int index) {         public E get(int index) {
    return elementData[index];        ListNode current = head;
}                                     // search for the ith element
                                      while (index > 0) {
                                          current = current.next;
                                          index--;
                                      }
                                      // return the value found
                                      return current.value;
                                  }
ArrayList efficiency LinkedList efficiency Winner
constant linear array list
     
     

2.4 getFirst

public E getFirst() {                       public E getFirst() {
    return elementData[0];                      return head.value;
}                                           }        
ArrayList efficiency LinkedList efficiency Winner
constant constant tie
     
     

2.5 getLast

public E getLast() {                                    public E getLast() {
    return elementData[elementCount - 1];                   return tail.value;
}                                                       }     
ArrayList efficiency LinkedList efficiency Winner
constant constant tie
     
     

2.6 set

public E set(int index, E element) {     public E set(int index, E value) {
    E previous = elementData[index];         ListNode current = head;
    elementData[index] = element;            // search for the ith element
    return previous;                         while (index > 0) {
}                                                current = current.next;
                                                 index--;
                                             }
                                             // get old value, update new value
                                             E result = current.value;
                                             current.value = value;
                                             return result;
                                         }
ArrayList efficiency LinkedList efficiency Winner
constant linear array list
     
     

2.7 addFirst

public void addFirst(E element) {                public void addFirst(E value) {
    ensureCapacity(elementCount + 1);                // construct a new element, making it the head
                                                     head = new ListNode(value, null, head);
    for (int i = elementCount; i > 0; i--) {         // fix tail, if necessary
        elementData[i] = elementData[i - 1];         if (tail == null) tail = head;
    }                                                count++;
                                                 }
    elementData[0] = element;                                          
    elementCount++;                                          
}                                             
ArrayList efficiency LinkedList efficiency Winner
linear constant linked list, though the array list will average to constant over many addFirsts
     
     

2.8 addLast

public void addLast(E element) {               public void addLast(E value) {
    ensureCapacity(elementCount + 1);              // construct new element
    elementData[elementCount] = element;           tail = new ListNode(value, tail, null);
    elementCount++;                                // fix up head
}                                                  if (head == null) head = tail;
                                                   count++;
                                               }
ArrayList efficiency LinkedList efficiency Winner
linear constant linked list
     
     

2.9 removeFirst

public E removeFirst() {                         public E removeFirst() {
    E result = elementData[0];                       ListNode temp = head;
    elementCount--;                                  head = head.next;
                                                     if (head != null) {
    for (int i = 0; i < elementCount; i++) {             head.prev = null;
        elementData[i] = elementData[i + 1];         } else {
    }                                                    tail = null; // remove final value
                                                     }
    return result;                                   count--;
}                                                    return temp.value;
                                                 }
ArrayList efficiency LinkedList efficiency Winner
linear constant linked list
     
     

2.10 removeLast

public E removeLast() {                            public E removeLast() {
    E result = elementData[elementCount - 1];           ListNode temp = tail;
    elementCount--;                                     tail = tail.prev;
    return result;                                      if (tail != null) {
}                                                           tail.next = null;
                                                        } else {
                                                            head = null; // remove final value
                                                        }
                                                        count--;
                                                        return temp.value;
                                                    }
ArrayList efficiency LinkedList efficiency Winner
constant constant tie
     
     

2.11 remove

public E remove(E value) {                               public E remove(E value) {
    for (int i = 0; i < elementCount; i++) {                 ListNode current = head;
        if (elementData[i].equals(value)) {                  while (current != null && !current.value.equals(value)) {
            E result = elementData[i];                           current = current.next;
            elementCount--;                                  }
                                                             if (current != null) {
            while (i < elementCount) {                           // fix next field of previous node
                elementData[i] = elementData[i + 1];             if (current.prev != null) {
                i++;                                                 current.prev.next = current.next;
            }                                                    } else {
                                                                     head = current.next;
            return result;                                       }
        }                                                        // fix prev field of next node
    }                                                            if (current.next != null) {
    return null;                                                     current.next.prev = current.prev;
}                                                                } else {
                                                                     tail = current.prev;
                                                                 }
                                                                 count--; // fewer elements
                                                                 return current.value;
                                                             }
                                                             // matching value not found
                                                             return null;
                                                         }
ArrayList efficiency LinkedList efficiency Winner
linear linear tie, though probably the linked list in practice
     
     

2.12 contains

public boolean contains(E value) {                public boolean contains(E value) {
    for (int i = 0; i < elementCount; i++) {          ListNode current = head;
        if (elementData[i].equals(value)) {           while (current != null && !current.value.equals(value)) {
            return true;                                  current = current.next;
        }                                             }
    }                                                 return current != null;
    return false;                                 }
}                                              
ArrayList efficiency LinkedList efficiency Winner
linear linear tie
     
     

3 Challenge problems

Attempt these if you have extra time:

  1. Write a public boolean equals(LinkedList<E> otherList) method for the DoublyLinkedList class that returns true if the list has all the same values in the same order as otherList.
  2. Write a method to reverse a doubly-linked list.
  3. How would you make the ArrayList shrink as elements are removed? (Just as it grows as elements are added.)
  4. Implement a public boolean hasDuplicates() method for a linked list that returns true if there are any duplicate elements in the list.
  5. Implement a public void replaceAll(E from, E to) method for a linekd list that changes all elements with the value from to have the value to.