ArrayList
vs LinkedList
1 List
Interface
- The
ArrayList
andLinkedList
both implement theList
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 |
---|---|---|
2.4 getFirst
public E getFirst() { public E getFirst() { return elementData[0]; return head.value; } }
ArrayList efficiency |
LinkedList efficiency |
Winner |
---|---|---|
2.5 getLast
public E getLast() { public E getLast() { return elementData[elementCount - 1]; return tail.value; } }
ArrayList efficiency |
LinkedList efficiency |
Winner |
---|---|---|
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 |
---|---|---|
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 |
---|---|---|
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 |
---|---|---|
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 |
---|---|---|
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 |
---|---|---|
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 |
---|---|---|
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 |
---|---|---|
3 Challenge problems
Attempt these if you have extra time:
- Write a
public boolean equals(LinkedList<E> otherList)
method for theDoublyLinkedList
class that returnstrue
if the list has all the same values in the same order asotherList
. - Write a method to reverse a doubly-linked list.
- How would you make the
ArrayList
shrink as elements are removed? (Just as it grows as elements are added.) - Implement a
public boolean hasDuplicates()
method for a linked list that returns true if there are any duplicate elements in the list. - Implement a
public void replaceAll(E from, E to)
method for a linekd list that changes all elements with the valuefrom
to have the valueto
.