LinkedList
IntermediateJava's doubly-linked list that implements both List and Deque — ideal for frequent insertions at both ends, but rarely better than ArrayList for random access.
Overview
Java's LinkedList is a doubly-linked list — each node holds a reference to both the previous and next node. It implements both List and Deque, making it usable as a list, stack, queue, and double-ended queue. The key advantage over ArrayList is O(1) insertion and removal at the head and tail without shifting. The disadvantage is O(n) random access (must walk from head or tail) and higher memory overhead (two references per node). For most use cases, ArrayDeque outperforms LinkedList as a queue/stack.
LinkedList as List & Deque
As a List, LinkedList supports all index-based operations but they are O(n) internally. It traverses from the closer end (head if index < size/2, tail otherwise) to reach the target node.
As a Deque (double-ended queue), LinkedList provides O(1) addFirst/addLast, removeFirst/removeLast, peekFirst/peekLast. These are the operations where LinkedList shines — building a queue or stack where all work happens at the ends.
import java.util.LinkedList;
import java.util.Deque;
import java.util.Queue;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
// ── As a List ─────────────────────────────────────────────────
list.add("banana"); // O(1) — adds at tail
list.add("cherry");
list.add(0, "apple"); // O(n) — must traverse to index 0
System.out.println(list); // [apple, banana, cherry]
System.out.println(list.get(1)); // banana — O(n) traversal
System.out.println(list.size()); // 3
// ── As a Deque (double-ended queue) ──────────────────────────
LinkedList<Integer> deque = new LinkedList<>();
deque.addFirst(2); // [2]
deque.addFirst(1); // [1, 2]
deque.addLast(3); // [1, 2, 3]
deque.addLast(4); // [1, 2, 3, 4]
System.out.println(deque.peekFirst()); // 1 — does not remove
System.out.println(deque.peekLast()); // 4 — does not remove
System.out.println(deque.removeFirst()); // 1
System.out.println(deque.removeLast()); // 4
System.out.println(deque); // [2, 3]
// ── As a Queue (FIFO) ─────────────────────────────────────────
Queue<String> queue = new LinkedList<>();
queue.offer("first");
queue.offer("second");
queue.offer("third");
System.out.println(queue.poll()); // first (removes head)
System.out.println(queue.peek()); // second (views head, no remove)
// ── As a Stack (LIFO) ─────────────────────────────────────────
Deque<String> stack = new LinkedList<>();
stack.push("a"); stack.push("b"); stack.push("c");
System.out.println(stack.pop()); // c (LIFO)
}
}LinkedList vs ArrayList vs ArrayDeque
When to pick which:
ArrayList — best default for lists; O(1) random access; cache-friendly; use unless you have specific reason not to LinkedList — only preferable when you need O(1) insertions/deletions at arbitrary positions AND you already have a reference to the node (not common in Java since you work with indices/iterators) ArrayDeque — best Stack and Queue; faster than LinkedList for head/tail ops because no node allocation; more cache-friendly
In practice: 95% of the time use ArrayList. For queues and stacks, use ArrayDeque.
import java.util.*;
public class CollectionComparison {
// Benchmark-style comparison (conceptual)
public static void main(String[] args) {
int N = 100_000;
// ArrayList — fast end-appending and random access
List<Integer> arrayList = new ArrayList<>(N);
for (int i = 0; i < N; i++) arrayList.add(i); // O(1) amortised
System.out.println(arrayList.get(N / 2)); // O(1) random access
// LinkedList — slower random access but O(1) ends
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < N; i++) linkedList.addLast(i); // O(1) each
System.out.println(linkedList.get(N / 2)); // O(n) — traverses
// ArrayDeque — fastest queue/stack, no null allowed
Deque<Integer> arrayDeque = new ArrayDeque<>(N);
for (int i = 0; i < N; i++) arrayDeque.offerLast(i); // O(1) amortised
System.out.println(arrayDeque.peekFirst()); // O(1)
// Memory: LinkedList uses ~40 bytes per node (object header + 2 refs + data ref)
// ArrayList uses ~4 bytes per reference + object overhead
// ArrayDeque uses ~4 bytes per reference in a circular array
// Summary printout
System.out.println("ArrayList random access: O(1)");
System.out.println("LinkedList random access: O(n)");
System.out.println("ArrayDeque head/tail: O(1)");
System.out.println("LinkedList head/tail: O(1) — but higher GC pressure");
}
}Iterator & ListIterator
LinkedList's ListIterator is the only way to traverse and mutate the list in O(n) total rather than O(n²). Each next()/previous() moves by one node. Calling listIterator.add() or listIterator.remove() during traversal is safe (no ConcurrentModificationException from the iterator itself) and O(1) per operation because the iterator holds a reference to the current node.
import java.util.LinkedList;
import java.util.ListIterator;
public class LinkedListIterator {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>(
java.util.List.of(1, 2, 3, 4, 5));
// ListIterator — bidirectional, supports add/remove/set during iteration
ListIterator<Integer> it = list.listIterator();
while (it.hasNext()) {
int val = it.next();
if (val % 2 == 0) {
it.remove(); // O(1) — unlinks the node
} else {
it.set(val * 10); // O(1) — updates in place
}
}
System.out.println(list); // [10, 30, 50]
// Reverse traversal
while (it.hasPrevious()) {
System.out.print(it.previous() + " "); // 50 30 10
}
System.out.println();
// ListIterator.add() — inserts before the next element
ListIterator<Integer> it2 = list.listIterator();
it2.next(); // move past 10
it2.add(15); // insert 15 after 10
System.out.println(list); // [10, 15, 30, 50]
}
}Interactive Visualization
Key Points to Remember
- LinkedList implements both List and Deque — it is a doubly-linked list
- O(1) addFirst/addLast/removeFirst/removeLast; O(n) get(index) — must traverse from nearest end
- Higher memory overhead than ArrayList: ~40 bytes per node vs ~4 bytes per reference slot
- Prefer ArrayDeque over LinkedList for stacks and queues — faster and more cache-friendly
- ListIterator allows O(1) add/remove/set during traversal without ConcurrentModificationException
- LinkedList is fail-fast just like ArrayList — do not structurally modify from outside while iterating
Practice LinkedList in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat interfaces does LinkedList implement?
When would you choose LinkedList over ArrayList?
What is the time complexity of LinkedList.get(index)?
What is the difference between ArrayDeque and LinkedList as a Queue?
How does LinkedList achieve O(1) insertion at both ends?
Ask Aria about LinkedList
Your personal AI tutor — ask anything about this concept