ArrayList vs LinkedList
ArrayList vs LinkedList complexity, memory layout, cache locality, middle inserts, iterator removals, queues, JMH-style benchmarking and Vector.
On this page
- Picture it first: apartment block vs scattered houses
- The complexity table interviewers expect
- Why ArrayList usually wins middle inserts
- Memory comparison with numbers
- When LinkedList genuinely wins
- Queues, stacks and the FIFO scenario
- A JMH-style benchmark to discuss, not worship
- What about Vector?
- Recommended defaults
- How to say it in the interview
⚖️ What is ArrayList vs LinkedList really asking?
It is the classic "array versus linked nodes" trade-off. ArrayList stores element references in a contiguous resizable array; LinkedList stores each element in a separate node with prev and next pointers. The interview trap is that LinkedList sounds better for middle inserts, but Java still has to walk to the middle first.
For this lesson, we'll focus on the comparison interviewers actually care about: Big-O, memory layout, middle inserts, queue choices, iterator removals, a benchmark shape you can trust, the playlist and FIFO scenario questions, and the legacy Vector follow-up. We'll use Java 21 behaviour, and we'll cross-link to the internals lessons when you want the deeper implementation story.
Below the line (out of scope)
ArrayListgrowth, capacity andmodCount: How ArrayList worksLinkedListnode internals and reversal: How LinkedList works- Queue and stack APIs: Queue, Deque and ArrayDeque
- General collection selection: Choosing the right collection
The 30-second answer
I default to ArrayList for lists. It has O(1) indexed access, amortised O(1) append, compact memory, and excellent cache locality. LinkedList has O(1) insertion or removal only when you already have the node position, usually through a ListIterator; add(index, e) and remove(index) are still O(n) because they walk to the index first. Even middle inserts often favour ArrayList, because System.arraycopy is a highly optimised memory move while LinkedList allocates nodes and chases pointers. For queues and stacks, use ArrayDeque unless you specifically need LinkedList's ability to hold null elements or a ListIterator workflow. Vector is the old synchronized array-backed list, and I do not choose it for new code.
What you’ll learn
- Compare
ArrayListandLinkedListwith a complexity table interviewers expect - Explain why
ArrayListoften wins middle inserts throughSystem.arraycopyand cache locality - Estimate memory use with compressed oops: 4-byte array slots vs roughly 24-byte LinkedList nodes
- Identify the real
LinkedListwin: removal or insertion through aListIteratoryou already hold - Choose
ArrayDequeoverLinkedListfor ordinary queues and stacks - Read and write a small JMH-style benchmark without treating illustrative numbers as universal claims
- Answer playlist, FIFO queue and
Vectorinterview follow-ups with nuance
Interview radar
Asked very often: Which is better, ArrayList or LinkedList, and why?
Senior rounds probe: Why Big-O alone misleads, how memory layout changes performance, and when LinkedList genuinely wins.
Production angle: A codebase picks LinkedList for "lots of inserts" and gets slower because every insert starts with a linear search plus a node allocation.
01Picture it first: apartment block vs scattered houses
ArrayList is an apartment block: one building, numbered slots, quick access by floor. LinkedList is a chain of houses spread across a city: each house has the address of the previous and next house, but to reach house 500 you follow directions step by step.
ArrayList stores references in one contiguous array while LinkedList stores scattered node objects with prev and next pointers
The diagram explains most real-world results. ArrayList pays when it has to grow or shift a suffix. LinkedList pays on almost every traversal, because each step loads another node object from wherever the allocator placed it.
02The complexity table interviewers expect
Start with the table, then immediately explain its limits. Big-O is the map, not the road surface.
| Operation | ArrayList |
LinkedList |
Interview note |
|---|---|---|---|
get(i) / set(i, e) |
O(1) | O(n) | linked list walks from nearer end |
| append at end | amortised O(1) | O(1) | array sometimes grows and copies |
| insert at front | O(n) | O(1) | array shifts every element |
| insert at index | O(n) | O(n) | linked list walks, then relinks |
| remove at index | O(n) | O(n) | linked list walks, then unlinks |
| remove via iterator at current position | O(n) shift | O(1) relink | the clean LinkedList win |
| contains / search | O(n) | O(n) | array iteration is usually friendlier to CPU caches |
| iteration | O(n) | O(n) | same Big-O, different constants and cache behaviour |
| queue ends | awkward at front | O(1) | ArrayDeque usually beats both |
Try it yourself first
You need to insert 10,000 items into the middle of a 1,000,000 element list by calling list.add(list.size() / 2, item) each time. Which list would you try first, and why?
Many candidates answer LinkedList because "insert in middle is O(1)." In that API call, the position is an index, not a node. LinkedList first performs an O(n) walk to the middle, then does O(1) pointer rewiring. ArrayList computes the index in O(1), then shifts a contiguous suffix with System.arraycopy.
List<String> ids = new ArrayList<>();
ids.add("A");
ids.add("C");
ids.add(1, "B"); // shifts C one slot to the right
List<String> linked = new LinkedList<>(ids);
linked.add(1, "A2"); // walks to index 1, then relinks
Notice what the table does not show: allocation, branch prediction, CPU cache misses, prefetching and GC. These are why the array-backed list often wins even for operations whose asymptotic cost looks worse.
03Why ArrayList usually wins middle inserts
Let's unpack the middle insert, because this is where interviews separate memorised Big-O from engineering judgement.
For ArrayList.add(index, e), Java checks capacity, shifts the suffix right by one with System.arraycopy, writes the new reference, and increments size. System.arraycopy is a JVM intrinsic on HotSpot for common cases, so it becomes a tuned native memory move. It moves references that sit next to each other in memory.
For LinkedList.add(index, e), Java calls node(index). That walks from first or last, whichever is closer, using the check index < size >> 1. Only after it reaches the successor node can it call linkBefore(e, succ) and update a few pointers. The walk is the expensive part.
// ArrayList-style work: find slot instantly, move a block
System.arraycopy(elements, index, elements, index + 1, size - index);
elements[index] = item;
// LinkedList-style work: find node first, then relink
Node<E> succ = node(index); // O(n) walk from first or last
linkBefore(item, succ); // O(1) after succ is known
With compressed oops, moving 100,000 references in an array means copying about 400 KB of pointer data. That can fit through modern memory bandwidth quickly and predictably. Walking 100,000 linked nodes means 100,000 dependent pointer loads, and each cache miss may cost on the order of 100 ns. Do not quote those as universal benchmark numbers; use them to explain why contiguous memory matters.
The practical rule is: if you locate positions by index, ArrayList is usually the first implementation to try. If you already have an iterator positioned at the edit point, LinkedList becomes interesting.
04Memory comparison with numbers
ArrayList has one list object plus an Object[] containing references. Under compressed oops, each reference slot is 4 bytes. A capacity of 1,000,000 slots is about 4 MB for the array payload, plus array header and padding. The elements themselves are not inside the array; the array stores references to them.
LinkedList has one list object plus one node object per element. A simplified LinkedList.Node has an object header and three references: item, next and prev.
| Structure | Approximate per-position overhead, compressed oops | What is not counted |
|---|---|---|
ArrayList occupied slot |
4 bytes | spare capacity, array header, element object |
LinkedList.Node |
about 24 bytes | element object, allocator/card-table effects |
| Extra pointer fields per linked node | 8 bytes for next and prev |
included in the 24-byte node estimate |
For 1,000,000 Integer references, the list storage difference is roughly 4 MB of ArrayList slots versus 24 MB of LinkedList nodes before counting the 1,000,000 Integer objects. If the elements are large domain objects, this ratio matters less. If the elements are small wrappers, IDs or references, the node overhead is visible in heap profiles.
Many candidates compare only time complexity and forget memory. A LinkedList does not store "only pointers" for free; it allocates one extra object per element. That increases heap usage, GC work and cache misses.
05When LinkedList genuinely wins
The clean win is editing at a cursor you already hold. A ListIterator over a LinkedList stores enough state to remove the last returned node or add before the next node without searching again.
Try it yourself first
In this playlist cleanup, which part is O(n), and which edits are O(1)?
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Queues, stacks and the FIFO scenario
- A JMH-style benchmark to discuss, not worship
- What about Vector?
- Recommended defaults
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal