Hook: Interviewers love this one because the “best” list is really about how data moves when you read, insert, and delete.
Question: ArrayList vs LinkedList.
Answer: ArrayList stores items in a growable array, so it is fast for index-based access like get(5) and usually faster to iterate. LinkedList stores items as separate nodes connected by links, so it is better when you do lots of insertions or removals at the ends and already have the position. In real Java code, ArrayList is the default choice most of the time.
Interview-Ready Answer: “I usually pick ArrayList because it gives fast random access and better iteration performance due to contiguous storage. LinkedList is a doubly linked list, so it can help for queue-style operations or frequent end insertions and removals, but it uses more memory and get(i) is linear time. In practice, unless I truly need deque behavior, I reach for ArrayList.”
Detailed Explanation: Think of ArrayList as a row of seats and LinkedList as a paper chain. A seat number gets you to the person instantly; a paper chain lets you add or remove links easily, but finding the 80th link means walking through the chain.
ArrayList keeps its elements in a backing Object[] array. The array is contiguous in memory, so get(i) is just direct indexing.ArrayList allocates a bigger array and copies the old elements over. That copy is why a single append can occasionally be expensive, but across many appends it is amortized O(1), meaning the average cost stays low over time.LinkedList stores each element in a separate Node. A node is a small object that keeps the value plus links to the previous and next nodes.get(i) on a LinkedList must walk node by node from the head or tail, whichever is closer, so it is O(n).for (int i = 0; i < list.size(); i++) list.get(i) is fine for ArrayList, but can become painfully slow for LinkedList.| Feature | ArrayList | LinkedList |
|---|---|---|
| Storage | Contiguous array | Node chain |
get(i) | O(1) | O(n) |
| Append at end | Amortized O(1) | O(1) |
| Insert/remove front | O(n) | O(1) |
| Iteration | Usually faster | Usually slower |
| Memory | Lower overhead | Higher overhead |
ArrayList when you read often, loop often, or need fast random access by index.LinkedList when you truly need deque behavior, such as frequent addFirst/removeFirst or offer/poll style operations.ArrayDeque is often better than LinkedList because it is usually smaller and faster.ArrayList grows by copying; the occasional resize costs time, but the average append is still very good.LinkedList has a large per-element cost because each node stores links in addition to the value. On a 64-bit JVM, that overhead can be tens of bytes per element, which matters at scale.ConcurrentModificationException if the list is structurally changed while you iterate without using the iterator’s own remove.LinkedList.size() is O(1) in Java, because it stores the count; many candidates wrongly think it must walk the list.Memory trick: the winner is usually the one with the fewest surprises. For most business code, that is ArrayList.
Real-World Example: Imagine a checkout service that keeps a list of pending orders to display in an admin dashboard. A developer switches from ArrayList to LinkedList because they expect lots of inserts, but the dashboard still reads order #500 by index and loops through the whole list on every refresh. The result is worse latency, more CPU time, and a bigger heap. In production, the symptoms look like p95 response time jumping from milliseconds to seconds, APM traces pointing at list traversal, and users seeing the dashboard time out while the logs keep showing normal business events. The bug is not just “wrong Big-O”; it is choosing a structure that fights the real access pattern.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
public class ArrayListVsLinkedListDemo {
public static void main(String[] args) {
// Both are List implementations, so they can hold the same data.
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
arrayList.addAll(Arrays.asList("A", "B", "C"));
linkedList.addAll(Arrays.asList("A", "B", "C"));
System.out.println("Initial ArrayList: " + arrayList);
System.out.println("Initial LinkedList: " + linkedList);
// Random access is the big strength of ArrayList.
// LinkedList can do it too, but it must walk node by node to reach the index.
System.out.println("ArrayList get(1): " + arrayList.get(1));
System.out.println("LinkedList get(1): " + linkedList.get(1));
// Inserting at the front forces ArrayList to shift elements right.
// LinkedList rewires links, so the operation itself is cheap once the position is known.
arrayList.add(0, "START");
linkedList.add(0, "START");
System.out.println("After add(0, \"START\"):");
System.out.println("ArrayList: " + arrayList);
System.out.println("LinkedList: " + linkedList);
// Removing from the front shows the same tradeoff in reverse.
arrayList.remove(0);
linkedList.remove(0);
System.out.println("After remove(0):");
System.out.println("ArrayList: " + arrayList);
System.out.println("LinkedList: " + linkedList);
// LinkedList also works as a queue because it implements Deque.
// For queue-style code, poll() is often safer than removeFirst() because it returns null on empty.
Deque<String> queue = new LinkedList<>();
System.out.println("Queue poll on empty: " + queue.poll());
queue.offer("job-1");
queue.offer("job-2");
System.out.println("Queue poll: " + queue.poll());
System.out.println("Queue poll: " + queue.poll());
System.out.println("Queue poll again: " + queue.poll());
// Edge case: bad indexes throw the same exception type in both lists.
// This reminds you that choosing the right list affects performance, not basic bounds checking.
List<String> empty = new ArrayList<>();
try {
System.out.println(empty.get(0));
} catch (IndexOutOfBoundsException ex) {
System.out.println("Edge case: " + ex.getClass().getSimpleName() + " when reading from an empty list.");
}
}
}Follow-up & Tricky Questions:
Deque interface.i.Common Mistakes:
LinkedList because it sounds better for insertions. Correction: if you still need to search for the position, the traversal cost usually wipes out the benefit.LinkedList for lots of get(i) calls. Correction: use ArrayList when random access matters.LinkedList uses less memory because it has no big array. Correction: it usually uses more memory because each element needs a separate node object and two links.ArrayDeque, not LinkedList. Correction: prefer the simpler, faster deque unless you have a strong reason not to.Memory Hook: ArrayList = seats in a theater; LinkedList = people holding hands in a line. Seats let you point to row 12, seat 5 instantly. Holding hands makes moving the ends easy, but finding the middle takes a walk.
Cheat Sheet:
ArrayList: fast get, fast iteration, good default choice.LinkedList: fast end insert/remove, poor random access.List implementations and both are not thread-safe.LinkedList.size() is O(1) in Java.ArrayDeque is often better than LinkedList.ArrayList.Practice Tasks:
for-each.Deque and compare LinkedList with ArrayDeque.LinkedList.get(i) calls in old code with a different data structure and observe the speedup.