LinkedList & the List Interface
LinkedList & the List Interface
You already know that ArrayList stores elements in a contiguous array. LinkedList takes the opposite approach: every element lives in its own node that holds a reference to the next and previous nodes. Understanding this internal difference is the whole story of when to choose one over the other.
The List Interface — the contract both share
Both ArrayList and LinkedList implement java.util.List<E>. That interface defines the operations every ordered, index-based collection must support:
add(E e)/add(int index, E e)— append or insertget(int index)— retrieve by positionset(int index, E e)— replace by positionremove(int index)/remove(Object o)— delete by index or valuesize(),contains(Object o),indexOf(Object o)subList(int from, int to),sort(Comparator<? super E> c)
Programming to the List interface rather than a concrete class is a core Java idiom. It lets you swap the implementation later without touching the rest of your code:
List, not ArrayList. Method signatures that accept List<String> work with any implementation. Signatures that demand ArrayList<String> lock you in — that is usually a mistake.
How LinkedList works internally
LinkedList is a doubly linked list. Each node looks roughly like this:
The list object keeps references to the first and last nodes only. To reach node number k, Java must walk the chain from one end — O(n) time. Inserting or deleting at a known node, however, is O(1): just rewire three pointers.
LinkedList also implements Deque<E>, which is why it exposes addFirst, addLast, peekFirst, pollLast, and so on — methods that ArrayList does not have.
ArrayList vs LinkedList — the trade-off table
These are the Big-O complexities that drive the decision:
- Random access (
get(i)): ArrayList O(1) vs LinkedList O(n). ArrayList wins decisively — it jumps straight to the backing array index. - Append at the end (
add(e)): Both are O(1) amortised. ArrayList occasionally resizes; LinkedList always allocates a new node. - Insert/delete in the middle (
add(i, e)/remove(i)): ArrayList O(n) because elements must shift; LinkedList O(n) to find the index, then O(1) to rewire. Neither wins clearly unless you already hold an iterator at the insertion point. - Insert/delete at the front: ArrayList O(n) (every element shifts right); LinkedList O(1). This is the one case LinkedList clearly outperforms ArrayList.
- Memory: ArrayList is more compact (one array). Each LinkedList node allocates two extra object references, making the per-element overhead 3–4× higher.
ArrayList by default; reach for LinkedList only when you specifically need O(1) front insertions/deletions or the Deque interface.
Iterating a LinkedList efficiently
Never use index-based loops on a LinkedList. Each get(i) call walks the chain from the head, turning an O(n) iteration into O(n²).
get(index) in a loop on a LinkedList. This is one of the most common performance mistakes Java developers make. Always iterate with the enhanced for-loop or an explicit Iterator / ListIterator.
Using LinkedList as a Deque
When you need a queue (FIFO) or a stack (LIFO) backed by a linked structure, LinkedList is a ready-made option:
Summary
The List interface defines the contract; ArrayList and LinkedList are two implementations with very different internal structures. Prefer ArrayList for general-purpose lists — it has better cache performance and is faster for random access. Use LinkedList when you need constant-time insertions at the front, or when you want a Deque without pulling in ArrayDeque. Always declare variables as List (or Deque) so the implementation stays swappable.