What Is a Java LinkedList?
Java's LinkedList is one of the standard implementations of the List interface, and the only one in java.util that is also a Deque. But what is a Java LinkedList exactly, how does it differ from an ArrayList, and what do you use when a list has to be shared across several machines?
What Is a Java LinkedList?
A Java LinkedList is a doubly linked list implementation supplied by the java.util package. Instead of storing elements side by side in a single block of memory, it stores each element in its own node, and every node holds a reference to the node before it and the node after it.
Its full declaration is:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
Implementing two interfaces at once is what makes the class unusual. As a Java list, a LinkedList is an ordered collection you can index into and iterate over. As a Java deque, it can also behave as a stack or a queue, with elements pushed and popped at either end.
A few other properties are worth knowing before you reach for it:
- Elements are held in insertion order.
- Duplicate elements are allowed, and so are null values.
- The list grows and shrinks at runtime, with no capacity to declare up front.
- It is not synchronized. If several threads touch the same LinkedList and any of them changes its structure, you have to synchronize externally, typically with
Collections.synchronizedList().
How Does a Java LinkedList Work?
Every entry in the list is a node containing three things: the element itself, a reference to the previous node, and a reference to the next node. The list object keeps a pointer to the first node and a pointer to the last one, which is why adding or removing at either end is immediate.
There is no index arithmetic. When you call get(5), the list walks the chain one node at a time until it arrives at position five. It does start from whichever end is closer to the requested index, but the work still grows with the size of the list.
Two constructors are available: an empty LinkedList(), and LinkedList(Collection<? extends E> c), which copies in the contents of an existing collection.
Alongside the familiar List methods, the Deque half of the class contributes a second set of operations:
- addFirst() / addLast(): Insert an element at the head or the tail.
- getFirst() / getLast(): Read the head or tail element without removing it.
- removeFirst() / removeLast(): Remove and return the head or tail element.
- peek() / poll(): Read or remove the head, returning null on an empty list rather than throwing.
-
push() / pop(): Stack-style operations, equivalent to
addFirst()andremoveFirst(). - offer(): The queue-style way to add to the tail. It returns false rather than throwing when a capacity-restricted queue is full, though a LinkedList is never bounded and so always returns true.
The pairs matter more than they look. removeFirst() throws NoSuchElementException when the list is empty, while pollFirst() returns null, so the one you choose determines how your code handles an empty queue.
ArrayList vs LinkedList
This is the comparison most developers are really asking about, and the honest answer is less flattering to LinkedList than its reputation suggests.
| Operation | LinkedList | ArrayList |
|---|---|---|
get(index) | O(n) | O(1) |
| Insert or remove at the head | O(1) | O(n) |
| Insert or remove at the tail | O(1) | Amortized O(1) |
| Insert or remove in the middle | O(n) | O(n) |
contains(element) | O(n) | O(n) |
| Memory per element | Higher | Lower |
Notice the middle row. LinkedList is often recommended for "frequent insertions," but inserting at an arbitrary index still requires walking to that index first. ArrayList has to shift the elements after the insertion point; LinkedList has to traverse to reach it. Neither is free.
Memory is the second consideration. An ArrayList keeps its elements in a contiguous array, so each element costs roughly one reference slot. A LinkedList node carries the element plus two extra references and its own object overhead. Contiguous storage is also friendlier to the CPU cache, while walking a linked list means chasing pointers to scattered locations on the heap.
When to Use a Java LinkedList
Use a LinkedList when the access pattern is genuinely head-and-tail:
- As a stack, queue, or double-ended queue, where every operation happens at one end or the other.
- When elements are constantly added and removed at the front, which is the one case ArrayList handles poorly.
- When you are already holding an iterator and removing through it, which avoids the traversal cost.
Prefer an ArrayList when you index into the collection, when the list is large enough that memory overhead matters, or when you simply are not sure. For most everyday code, ArrayList is the better default, and the Deque behavior is the strongest reason to choose LinkedList over it.
The Limits of a LinkedList in a Distributed Application
Every property described so far applies to a single object on a single heap, and that is where the class stops being useful.
A LinkedList exists inside one JVM. Another service, another container, or a second instance of the same application behind a load balancer cannot see it. When the process restarts, the list is gone. There is no expiration, no eviction policy, and no way to bound its memory except by managing it yourself. And because the class is unsynchronized, sharing one even between threads in a single process requires deliberate work.
None of this is a flaw in the design. It is simply the boundary of an in-process collection, and it is the boundary most applications hit as soon as they are deployed on more than one node.
Java LinkedList Alternatives with Valkey and Redis
The usual answer is to move the collection out of the heap and into a shared data store. Valkey and Redis both provide a native list type that is ordered by insertion and accessible from either end, which lines up closely with what a LinkedList does. Redisson, a Valkey and Redis Java client, exposes that type through the standard Java collection interfaces so the calling code barely changes.
Because a LinkedList implements two interfaces, it maps onto two Redisson objects. RList extends java.util.List and covers the ordered, indexable half:
RList<SomeObject> list = redisson.getList("anyList");
list.add(new SomeObject());
list.get(0);
list.remove(new SomeObject());
RDeque extends java.util.Deque and covers the head-and-tail half, which is the behavior most LinkedList users actually want:
RDeque<SomeObject> deque = redisson.getDeque("anyDeque");
deque.addFirst(new SomeObject());
deque.addLast(new SomeObject());
SomeObject head = deque.pollFirst();
SomeObject tail = deque.pollLast();
If a consumer should wait for an element rather than receive null from an empty collection, RBlockingDeque and RBlockingQueue implement the blocking variants from java.util.concurrent. Each of these objects is shared by every application instance connected to the same Valkey or Redis deployment, survives a restart of any single node, and can be given a TTL.
Three practical notes. Elements are stored remotely, so they have to be serialized; Redisson handles this through a configurable codec. A single list is capped at 4,294,967,295 (2³² − 1) elements. And every one of these objects also ships with asynchronous, reactive, and RxJava3 interfaces, so a shared collection does not force you back into blocking calls.
For coordinating changes across instances, Redisson adds distributed locks and transactions, plus per-object listeners that fire when elements are added, removed, or updated.
Frequently Asked Questions
Is a Java LinkedList Singly or Doubly Linked?
Doubly linked. Each node references both its predecessor and its successor, which is what allows constant-time removal at the tail as well as the head.
Is a Java LinkedList Thread-Safe?
No. It is unsynchronized, and concurrent structural modification will corrupt it. Wrap it with Collections.synchronizedList(), use a concurrent collection from java.util.concurrent, or move to a distributed object designed for shared access.
Which Is Faster, ArrayList or LinkedList?
It depends entirely on the operation. ArrayList is faster for indexed access and uses less memory; LinkedList is faster at adding and removing at the front. For general-purpose use, ArrayList is the safer default.
Can a LinkedList Be Shared Between JVMs?
Not directly. A LinkedList lives on one heap. To share an ordered collection across processes or machines, you need a distributed implementation such as Redisson's RList or RDeque backed by Valkey or Redis.
To see how the rest of the Java collection interfaces map onto Valkey and Redis, read Redis Data Structures in Java or browse the Redisson collections documentation.