Course contents

How LinkedList works

LinkedList internals, doubly linked nodes, List and Deque APIs, iterator edits, reversal techniques, cache and memory costs.

PremiumOften askedLesson15 min readCoreReviewed Oct 2026 · Java 21
On this page
  1. Picture it first: a train with two station signs
  2. LinkedList under the hood
  3. List, Deque and the missing RandomAccess marker
  4. Reversing a LinkedList in Java
  5. Performance and memory cost
  6. How to say it in the interview

🔗 What is a Java LinkedList?

A LinkedList is a List whose elements live in separate node objects, and each node points to the previous and next node. Think of it like a train: every carriage knows the carriage in front and behind it, while the railway office keeps pointers to the first and last carriage. If you insert a new song between two playlist entries, the list changes a few arrows instead of shifting a whole array.

For this lesson, we'll focus on how Java 21's java.util.LinkedList is built, why it implements both List and Deque, how its node operations work, what node(index) really costs, and how to reverse linked lists in both library code and interview code. Interviewers ask this because the class looks easy, but the real test is whether you can separate pointer rewiring from finding the position.

Below the line (out of scope)

The 30-second answer

Java's LinkedList is a doubly linked list. Internally it has first, last, size, and nodes shaped like Node<E> { E item; Node<E> next; Node<E> prev; }. Adding or removing at either end is O(1), and inserting next to a ListIterator you already hold is O(1), because it only changes pointers. But get(i), add(i, e) and remove(i) first call node(i), which walks from first or last depending on whether i < size >> 1, so indexed operations are O(n). It implements List and Deque, not RandomAccess. In practice, the extra node objects, pointer chasing and cache misses mean ArrayList usually wins unless you already have the iterator position or need deque operations with null elements.

What you’ll learn

  • Draw Java LinkedList as **doubly linked Node<E> objects** with first, last and size
  • Explain linkFirst, linkLast, linkBefore, unlink and why indexed access uses node(index)
  • Compare LinkedList as a List and as a Deque, including why it does not implement RandomAccess
  • Reverse a linked list with Collections.reverse, descendingIterator, Java 21 reversed() and hand-written node algorithms
  • Use a ListIterator for O(1) insertion at an existing cursor and know when the walk still costs O(n)
  • Estimate per-node memory cost and explain pointer chasing, cache misses and why ArrayList often wins

Interview radar

Asked often: Explain how LinkedList works internally, why random access is slow, and how you reverse a linked list. Senior rounds probe: Why middle insertion is not automatically O(1), what Java 21's reversed() returns, and why memory layout beats Big-O tables in real workloads. Production angle: A service stores millions of small entries in a LinkedList and spends more time chasing pointers and collecting node objects than doing useful work.

01Picture it first: a train with two station signs

A Java LinkedList is not one big object containing all elements. It is a small list object that points to a chain of node objects. The list object keeps two station signs, first and last, so operations at either end do not need a scan.

Fig. 1

A LinkedList object stores first, last and size, and each node points to its previous and next node in a doubly linked chain

A LinkedList object stores first, last and size, and each node points to its previous and next node in a doubly linked chain

Notice three things in the picture. Each element has its own wrapper node. The arrows go both ways, so removing a node can stitch its neighbours together. There is no array index to jump through, so reaching node 7 means walking node by node.

02LinkedList under the hood

The shape below is simplified from OpenJDK, but it keeps the important fields. The real class also has modCount from AbstractList, used by fail-fast iterators.

Try it yourself first

Before reading the notes, answer this: which operations in the code below need to walk through the list, and which only touch the ends?

import java.util.LinkedList;
import java.util.ListIterator;

LinkedList<String> queue = new LinkedList<>();
queue.addLast("auth");
queue.addLast("payment");
queue.addFirst("edge");

String head = queue.removeFirst();       // edge
String middle = queue.get(1);            // payment, found by walking

ListIterator<String> it = queue.listIterator(1);
it.add("fraud-check");                  // insert at cursor
System.out.println(queue);               // [auth, fraud-check, payment]

Notice the split. addFirst, addLast, removeFirst and removeLast are O(1). get(1) and listIterator(1) must find the indexed node first. Once the iterator is sitting at the cursor, it.add(...) is O(1).

The internal node is this idea:

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E item, Node<E> next) {
        this.item = item;
        this.next = next;
        this.prev = prev;
    }
}

The main list object has three fields that matter for structure: first, last and size. These work together through four primitive operations.

Operation What it changes Cost
linkFirst(e) create a node before old first, update first, maybe last O(1)
linkLast(e) create a node after old last, update last, maybe first O(1)
linkBefore(e, succ) create a node between succ.prev and succ O(1) after succ is known
unlink(x) detach x, connect x.prev to x.next, clear fields O(1) after x is known

Here is a readable version of linkBefore. The important phrase is after succ is known.

private void linkBefore(E item, Node<E> succ) {
    Node<E> pred = succ.prev;
    Node<E> newNode = new Node<>(pred, item, succ);
    succ.prev = newNode;

    if (pred == null) {
        first = newNode;
    } else {
        pred.next = newNode;
    }
    size++;
}

This is where many Big-O explanations go wrong. Inserting beside a node is O(1). Inserting at index 50_000 is not, because Java must first locate index 50_000.

The node(index) trick: walk from the nearer end

LinkedList does not always start from the head. If the index is in the first half, it walks forward from first; otherwise it walks backward from last.

Node<E> node(int index) {
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++) x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--) x = x.prev;
        return x;
    }
}

For a list of 1,000,000 elements, get(10) walks 10 links, while get(999_990) walks 9 links backward. get(500_000) still walks about 500,000 links. The optimisation halves the worst walk, but it does not make indexed access constant time.

Where candidates slip

Many candidates say, "LinkedList is O(1) for middle insertions." That is only true if you already hold the node or a ListIterator at that position. If the API call is list.add(index, value), the list first pays O(n) to reach the index, then O(1) to relink.

03List, Deque and the missing RandomAccess marker

LinkedList implements List<E> and Deque<E>. As a List, it preserves insertion order, permits duplicates, permits null, and exposes indexed methods like get, set, add(index, e) and remove(index). As a Deque, it supports queue and stack methods at both ends: addFirst, addLast, removeFirst, removeLast, offer, poll, push, pop and peek.

It does not implement RandomAccess. That marker tells algorithms, "indexed access is cheap." ArrayList implements it; LinkedList does not. Library code can use that signal to choose an indexed loop for array-backed lists and an iterator walk for linked lists.

static long sum(List<Integer> values) {
    long total = 0;
    if (values instanceof java.util.RandomAccess) {
        for (int i = 0; i < values.size(); i++) total += values.get(i);
    } else {
        for (int value : values) total += value;
    }
    return total;
}

That branch is not about style. On a LinkedList, for (int i = 0; i < n; i++) list.get(i) is O(n^2), because every get(i) starts a new walk.

In the real world

I once reviewed a batch job that loaded about 600,000 IDs into a LinkedList, then used an index loop because it wanted progress logging every 10,000 rows. The code looked harmless and passed tests with 100 IDs. In production it spent minutes walking the same links again and again. Switching to an enhanced for loop fixed the algorithm; switching the storage to ArrayList fixed the memory and cache behaviour too.

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.

  • Reversing a LinkedList in Java
  • Performance and memory cost
  • How to say it in the interview

From ₹199 · one-time payment · No auto-renewal

Practise this topicLists: interview questions & scenarios
Further reading (9)