00:00

LinkedList in Java – Complete Guide with Examples

Last updated: 13 min read

LinkedList is a list in which every element is stored in its own small object, called a node, and each node points to the node before it and the node after it. It lives in the java.util package, has been part of the Collections Framework since JDK 1.2, and implements both the List and Deque interfaces.

Because of that double role, one class can act as a normal list, a queue, a stack or a double-ended queue. In this tutorial you will learn how LinkedList works, how to use its methods, and, just as important, when you should not use it.

1. What Is a LinkedList?

Picture a train. Each coach carries passengers (the data) and is coupled to the coach in front and the coach behind. To add a coach in the middle, you only uncouple two coaches and couple in the new one; nothing else moves. But to reach coach 40, you have to walk through coaches 1 to 39.

A LinkedList works the same way:

  • Doubly linked: each node holds the element, a link to the previous node and a link to the next node.
  • Head and tail: the list remembers its first and last node, so adding or removing at either end is instant.
  • Ordered: it keeps insertion order and gives each element an index.
  • Duplicates and null allowed, just like ArrayList.
  • No capacity: it never needs to resize; it creates one node per element.
  • Not synchronized: it is not thread-safe.

2. Class Hierarchy and Declaration

LinkedList extends AbstractSequentialList and implements List, Deque, Cloneable and Serializable. Notice that it does not implement RandomAccess, which is the hint that reading by index is slow.

LinkedList class hierarchy in Java
java.util.LinkedList
public class LinkedList<E> extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

3. Your First LinkedList Program

This small playlist shows the methods that make LinkedList special: addFirst(), addLast(), getFirst() and getLast().

LinkedListQuickStart.java
import java.util.LinkedList;

public class LinkedListQuickStart {
    public static void main(String[] args) {
        LinkedList<String> playlist = new LinkedList<>();

        playlist.add("Song B");          // add at the end
        playlist.addFirst("Song A");     // add at the beginning
        playlist.addLast("Song C");      // add at the end

        System.out.println("Playlist: " + playlist);
        System.out.println("First:    " + playlist.getFirst());
        System.out.println("Last:     " + playlist.getLast());
        System.out.println("Size:     " + playlist.size());
    }
}
Output
Playlist: [Song A, Song B, Song C]
First:    Song A
Last:     Song C
Size:     3

4. Constructors

ConstructorWhat it does
LinkedList()Creates an empty list.
LinkedList(Collection<? extends E> c)Creates a list containing the elements of another collection, in its iteration order.

There is no constructor that takes a capacity, because a linked list has no internal array to size.

5. Adding, Reading and Updating Elements

LinkedList supports every List method, plus extra methods for the two ends of the list.

LinkedListAddGet.java
import java.util.LinkedList;
import java.util.List;

public class LinkedListAddGet {
    public static void main(String[] args) {
        LinkedList<Integer> numbers = new LinkedList<>(List.of(20, 30));

        numbers.addFirst(10);            // [10, 20, 30]
        numbers.addLast(50);             // [10, 20, 30, 50]
        numbers.add(3, 40);              // insert at index 3
        numbers.addAll(List.of(60, 70)); // add a collection at the end
        System.out.println("List:          " + numbers);

        System.out.println("get(2):        " + numbers.get(2));
        System.out.println("getFirst():    " + numbers.getFirst());
        System.out.println("getLast():     " + numbers.getLast());
        System.out.println("indexOf(40):   " + numbers.indexOf(40));
        System.out.println("contains(99):  " + numbers.contains(99));

        numbers.set(0, 5);               // replace the first element
        System.out.println("After set:     " + numbers);
    }
}
Output
List:          [10, 20, 30, 40, 50, 60, 70]
get(2):        30
getFirst():    10
getLast():     70
indexOf(40):   3
contains(99):  false
After set:     [5, 20, 30, 40, 50, 60, 70]
Note: get(2) works, but it is not instant. The list walks node by node from whichever end is closer (the head or the tail) until it reaches index 2.

6. Removing Elements

Besides remove(index) and remove(Object), LinkedList can remove from either end and can remove the last occurrence of a value.

LinkedListRemove.java
import java.util.LinkedList;
import java.util.List;

public class LinkedListRemove {
    public static void main(String[] args) {
        LinkedList<String> tasks = new LinkedList<>(
                List.of("Login", "Search", "Cart", "Search", "Pay", "Logout"));
        System.out.println("Start:                    " + tasks);

        tasks.removeFirst();
        System.out.println("removeFirst():            " + tasks);

        tasks.removeLast();
        System.out.println("removeLast():             " + tasks);

        tasks.remove(1);                       // by index
        System.out.println("remove(1):                " + tasks);

        tasks.removeLastOccurrence("Search");
        System.out.println("removeLastOccurrence():   " + tasks);

        tasks.removeIf(t -> t.startsWith("P"));
        System.out.println("removeIf(P*):             " + tasks);
    }
}
Output
Start:                    [Login, Search, Cart, Search, Pay, Logout]
removeFirst():            [Search, Cart, Search, Pay, Logout]
removeLast():             [Search, Cart, Search, Pay]
remove(1):                [Search, Search, Pay]
removeLastOccurrence():   [Search, Pay]
removeIf(P*):             [Search]

removeFirst() and removeLast() throw NoSuchElementException on an empty list. Their safe versions, pollFirst() and pollLast(), return null instead.

7. Using LinkedList as a Queue

A queue serves elements in the order they arrive: first in, first out (FIFO), like people waiting at a ticket counter. LinkedList implements Queue, so you can use offer() to join the queue, peek() to look at the front, and poll() to take the front element.

LinkedListAsQueue.java
import java.util.LinkedList;
import java.util.Queue;

public class LinkedListAsQueue {
    public static void main(String[] args) {
        // Queue: first in, first out (FIFO)
        Queue<String> printQueue = new LinkedList<>();
        printQueue.offer("report.pdf");
        printQueue.offer("photo.png");
        printQueue.offer("notes.txt");

        System.out.println("Next job (peek): " + printQueue.peek());
        while (!printQueue.isEmpty()) {
            System.out.println("Printing " + printQueue.poll());
        }
        System.out.println("poll() on empty queue: " + printQueue.poll());
    }
}
Output
Next job (peek): report.pdf
Printing report.pdf
Printing photo.png
Printing notes.txt
poll() on empty queue: null

8. Using LinkedList as a Stack and Deque

A stack is last in, first out (LIFO), like the browser's Back button. push() adds to the front and pop() removes from the front. Because LinkedList is a Deque (double-ended queue), it can also add and remove at both ends.

LinkedListAsStack.java
import java.util.Deque;
import java.util.LinkedList;

public class LinkedListAsStack {
    public static void main(String[] args) {
        // Stack: last in, first out (LIFO)
        Deque<String> history = new LinkedList<>();
        history.push("home");
        history.push("products");
        history.push("product/42");

        System.out.println("History: " + history);
        System.out.println("Back from " + history.pop());
        System.out.println("Now on    " + history.peek());

        // Deque: add and remove at both ends
        Deque<Integer> deque = new LinkedList<>();
        deque.offerFirst(2);
        deque.offerFirst(1);
        deque.offerLast(3);
        System.out.println("Deque:   " + deque);
        System.out.println("pollLast(): " + deque.pollLast() + ", pollFirst(): " + deque.pollFirst());
    }
}
Output
History: [product/42, products, home]
Back from product/42
Now on    products
Deque:   [1, 2, 3]
pollLast(): 3, pollFirst(): 1
Tip: for stacks and queues, ArrayDeque is usually faster than LinkedList because it stores elements in an array and creates no node objects. Use LinkedList when you also need null elements or List methods such as get(index).

9. Iterating Forward and Backward

Because each node links to the previous one, LinkedList can walk backwards just as easily as forwards. A ListIterator can also insert or remove at its current position in constant time, which is where LinkedList really shines.

LinkedListIterate.java
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

public class LinkedListIterate {
    public static void main(String[] args) {
        LinkedList<String> stops = new LinkedList<>(List.of("A", "B", "C", "D"));

        // Forward with for-each
        for (String s : stops) System.out.print(s + " ");
        System.out.println();

        // Backward with descendingIterator()
        Iterator<String> back = stops.descendingIterator();
        while (back.hasNext()) System.out.print(back.next() + " ");
        System.out.println();

        // Insert while looping with ListIterator: O(1) per insert
        ListIterator<String> it = stops.listIterator();
        while (it.hasNext()) {
            if (it.next().equals("B")) {
                it.add("B2");            // inserted right after B
            }
        }
        System.out.println("After insert: " + stops);
    }
}
Output
A B C D 
D C B A 
After insert: [A, B, B2, C, D]

10. Common LinkedList Methods

Many methods come in pairs: one throws an exception when the list is empty, the other returns a special value (null or false).

PurposeThrows exceptionReturns null/false
Add at the frontaddFirst(e), push(e)offerFirst(e)
Add at the endaddLast(e), add(e)offerLast(e), offer(e)
Remove from the frontremoveFirst(), remove(), pop()pollFirst(), poll()
Remove from the endremoveLast()pollLast()
Read the frontgetFirst(), element()peekFirst(), peek()
Read the endgetLast()peekLast()

Other useful methods:

MethodDescription
add(int index, E e)Inserts at a position.
get(int index), set(int index, E e)Read or replace by index (walks the list).
remove(int index), remove(Object o)Remove by position or by value.
removeFirstOccurrence(o), removeLastOccurrence(o)Remove the first or last matching value.
indexOf(o), lastIndexOf(o), contains(o)Search for a value.
descendingIterator()Iterates from the last element to the first.
listIterator(int index)Iterator that can move both ways and add, set or remove.
size(), isEmpty(), clear()Count, check and empty the list.
reversed() (Java 21+)Returns a reversed view of the list.

11. How LinkedList Works Internally

Inside the JDK, each element is wrapped in a private Node class:

LinkedList.Node (simplified)
private static class Node<E> {
    E item;        // the element
    Node<E> next;  // the node after this one
    Node<E> prev;  // the node before this one
}

The list itself keeps three fields: first (the head), last (the tail) and size.

  1. Adding at the end: create a node, point the old tail's next at it, and make it the new tail. No copying.
  2. Adding at the front: the same at the head.
  3. Inserting in the middle: first walk to the position (slow), then change four links (fast).
  4. Reading by index: walk from the head if the index is in the first half, otherwise from the tail. In the worst case that is size / 2 steps.

Each node costs extra memory for the two links and the object header, so a LinkedList uses several times more memory than an ArrayList with the same elements.

12. Time Complexity

OperationLinkedListArrayList
Add or remove at the frontO(1)O(n)
Add or remove at the endO(1)O(1) amortized
get(i), set(i, e)O(n)O(1)
Insert or remove at index iO(n) to find + O(1) to linkO(n) to shift
Insert or remove with a ListIteratorO(1)O(n)
contains(e), indexOf(e)O(n)O(n)

The biggest trap is looping with an index. Each get(i) walks the list again, so the loop below is O(n²) for a LinkedList:

IndexLoopCost.java
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class IndexLoopCost {
    static long sumByIndex(List<Integer> list) {
        long sum = 0;
        for (int i = 0; i < list.size(); i++) {
            sum += list.get(i);          // O(1) for ArrayList, O(n) for LinkedList
        }
        return sum;
    }

    public static void main(String[] args) {
        List<Integer> array = new ArrayList<>();
        List<Integer> linked = new LinkedList<>();
        for (int i = 0; i < 50_000; i++) { array.add(i); linked.add(i); }

        long t1 = System.nanoTime();
        sumByIndex(array);
        long t2 = System.nanoTime();
        sumByIndex(linked);
        long t3 = System.nanoTime();

        long arrayMs = (t2 - t1) / 1_000_000, linkedMs = (t3 - t2) / 1_000_000;
        System.out.println("LinkedList much slower with get(i)? " + (linkedMs > arrayMs * 5));
    }
}
Output
LinkedList much slower with get(i)? true

Use a for-each loop or an iterator instead, which visits each node exactly once.

13. LinkedList vs ArrayList

FeatureLinkedListArrayList
Internal structureDoubly linked nodesResizable array
ImplementsList, DequeList, RandomAccess
Access by indexSlow, O(n)Fast, O(1)
Add or remove at the frontFast, O(1)Slow, O(n)
Memory per elementHigh (node + two links)Low
CPU cache friendlinessPoor (nodes are scattered)Good (contiguous array)
Can be a queue or stackYesNo

In practice: ArrayList wins for most programs, even ones that insert now and then, because walking to the insert position in a LinkedList is slow. Choose LinkedList when you add and remove mainly at the ends and need List methods, or when you insert and remove through a ListIterator while walking the list.

14. LinkedList and Thread Safety

LinkedList is not synchronized. For a shared list, wrap it with Collections.synchronizedList(new LinkedList<>()). For a shared queue, use a class built for concurrency instead, such as ConcurrentLinkedQueue, ConcurrentLinkedDeque or LinkedBlockingQueue (which also lets consumers wait for new elements).

15. Common Mistakes

MistakeProblemBetter
Looping with get(i)O(n²) running timeUse for-each or an iterator.
Choosing LinkedList "because inserts are fast"Finding the position is still O(n)Measure; ArrayList is usually faster.
Calling getFirst() or pop() on an empty listNoSuchElementExceptionCheck isEmpty() or use peekFirst()/pollFirst().
Using LinkedList as a simple stack or queueExtra memory and garbageUse ArrayDeque.
Declaring the variable as List and then needing addFirst()Method not visibleDeclare it as Deque or LinkedList.

16. Summary

  • LinkedList is a doubly linked list that implements both List and Deque.
  • Adding and removing at either end is O(1); reading or writing by index is O(n).
  • It can be used as a list, queue, stack or deque.
  • It uses more memory than ArrayList and is slower for most everyday tasks.
  • Avoid index-based loops; use for-each, Iterator or ListIterator.
  • For pure stacks and queues, prefer ArrayDeque.

Related Tutorials

Reference: Java SE 21 API: java.util.LinkedList