What is the difference between ArrayList and LinkedList?
ArrayList and LinkedList are both implementations of the List interface in Java, but they are backed by fundamentally different data structures.
ArrayList is backed by a dynamically resizing array. Elements are stored sequentially in contiguous memory, giving O(1) random access. When the array reaches capacity, ArrayList allocates a new array approximately 1.5x the size and copies all elements, making add() amortized O(1) at the end. Insertion or removal at arbitrary positions is O(n) because elements must be shifted.
LinkedList is a doubly-linked list. Each element is wrapped in a Node object with pointers to the previous and next nodes. Insertion and removal at head or tail is O(1). Accessing an element by index is O(n) because you must traverse from the head or tail. LinkedList also implements the Deque interface.
The textbook analysis suggests LinkedList is better for frequent insertions in the middle. In practice, ArrayList benefits enormously from cache locality — contiguous memory enables CPU prefetching, and sequential access is extremely fast. LinkedList nodes are scattered across the heap, causing frequent cache misses. The Node object overhead (24+ bytes per node for object header and two pointers) also adds up. Benchmarks consistently show ArrayList outperforms LinkedList for most real-world workloads.
LinkedList is genuinely appropriate when used strictly as a queue or deque, or when removing elements during iteration with ListIterator.remove(). However, even for queue operations, ArrayDeque is often better due to contiguous memory.
In practice: default to ArrayList. Use LinkedList only when you specifically need Deque operations and have measured that it performs better.
| Aspect | ArrayList | LinkedList |
|---|---|---|
| Backing structure | Dynamic array (contiguous memory) | Doubly-linked nodes (heap-scattered) |
| Random access (get) | O(1) | O(n) |
| Add at end | Amortized O(1) | O(1) |
| Insert/remove at middle | O(n) — element shifting | O(1) if at iterator position, O(n) to find position |
| Memory overhead | Unused array capacity | Node object per element (~24+ bytes extra) |
| Cache performance | Excellent (contiguous memory) | Poor (scattered nodes, frequent cache misses) |
| Additional interfaces | RandomAccess | Deque |
Correctly states the Big-O differences and identifies the underlying data structures. May not discuss cache locality.
Covers Big-O for all key operations, explains underlying data structures, discusses cache locality and why ArrayList wins in practice. Mentions memory overhead, ArrayDeque as an alternative.
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment