LinkedList in Java
LinkedList is a doubly linked list implementation in the Java Collections Framework. It
implements both the List and Deque interfaces, which makes it more flexible than a
simple list. It can behave like an ordered list, a queue, a deque, or a stack-style structure depending on the
methods used. This dual role is one of the main reasons LinkedList appears frequently in Java
interviews.
Unlike ArrayList, which stores elements in a resizable internal array, LinkedList
stores elements as connected nodes. Each node contains the element data and references to neighboring nodes.
Because nodes are linked together, inserting or removing at the beginning and end can be efficient. However,
accessing an element by index is slower because the list must move from node to node until it reaches the
requested position.
Understanding LinkedList is not just about remembering that it is a linked structure. The real
skill is knowing when that structure helps and when it does not. Many beginners assume
LinkedList is always faster for insertions and deletions, but performance depends on where the
operation happens and whether the target node is already known. A mature explanation compares access pattern,
modification pattern, memory overhead, and queue/deque requirements.
What Is LinkedList?
LinkedList is a class in the java.util package that stores elements in insertion
order and allows duplicate values. Because it implements List, it supports list behavior such as
add, get, remove, and iteration. Because it implements
Deque, it also supports methods such as addFirst, addLast,
removeFirst, removeLast, offer, poll, peek,
push, and pop.
A linked list is useful when data must be frequently added or removed from the ends of the structure, or when
queue and deque behavior is needed. It is less suitable when the application repeatedly accesses elements by
index, because index access requires traversal. This is the central tradeoff: LinkedList is
flexible for structural changes and deque operations, but weaker for random access.
LinkedList<String> list = new LinkedList<>();
Position in Collection Hierarchy
LinkedList has an interesting position in the collection hierarchy because it belongs to both the
list family and the deque family. As a List, it represents an ordered sequence that allows
duplicates. As a Deque, it supports insertion and removal from both ends. This allows one class
to support multiple data-structure patterns.
Iterable
|-- Collection
|-- List
| |-- LinkedList
|-- Deque
|-- LinkedList
In practice, the reference type should match the behavior needed. If a method only needs list behavior, it can
use List<T>. If it needs queue or deque behavior, Deque<T> may be a
better reference type. This keeps APIs focused and avoids exposing unnecessary operations.
Key Characteristics of LinkedList
The defining characteristic of LinkedList is node-based storage. Each element is stored inside a
node, and each node maintains links to the previous and next nodes. This makes insertion and deletion
structurally different from ArrayList. Instead of shifting many elements in an array, a linked
list can adjust node references.
The benefit of this structure is most visible at the beginning or end of the list. Adding or removing the first or last element can be efficient because the list keeps references to both ends. The limitation is random access. To reach element 500, the list must traverse through nodes until it reaches that position. There is no direct array-style jump to an index.
LinkedList also uses more memory per element than ArrayList because each node stores
extra references. This overhead matters when the list is very large. Choosing LinkedList should
therefore be based on behavior, not only on the idea that insertion and deletion are fast.
How LinkedList Works Internally
Internally, Java's LinkedList is a doubly linked list. A node contains three main parts: the
stored element, a reference to the previous node, and a reference to the next node. The list also maintains
references to the first and last nodes, allowing efficient operations at both ends.
prev <- [data] -> next
When a new node is inserted, the neighboring references are adjusted. When a node is removed, the surrounding nodes are linked to each other. This means the list does not need to shift a block of elements as an array would. However, finding the node may still require traversal. This is why insertion and deletion are not automatically fast in every scenario.
The internal node structure also explains the memory tradeoff. Every stored value requires not only the value reference, but also previous and next references. For small lists this overhead usually does not matter. For very large lists, it can become significant compared with an array-backed list.
Creating a LinkedList
A LinkedList can be created with its default constructor. It starts empty and grows as elements
are added. Since LinkedList implements multiple interfaces, the variable can be declared as
LinkedList, List, Deque, or Queue depending on the
intended use.
LinkedList<Integer> list = new LinkedList<>();
If the code needs list-specific methods such as get(index), a List reference may be
appropriate. If the code needs addFirst, removeLast, push, or
poll, a Deque reference communicates the purpose more clearly.
Commonly Used LinkedList Methods
LinkedList supports ordinary list operations and deque operations. Standard list methods include
adding, removing, accessing, searching, and iterating elements. Deque methods make it useful for queue-like
and stack-like workflows.
Adding Elements
list.add(10);
list.addFirst(5);
list.addLast(20);
Removing Elements
list.remove(); // first element
list.removeFirst();
list.removeLast();
Accessing Elements
list.get(0);
list.getFirst();
list.getLast();
Queue / Deque Operations
list.offer(30);
list.poll();
list.peek();
These methods allow LinkedList to support FIFO and LIFO behavior. FIFO means first-in,
first-out, which is queue-like behavior. LIFO means last-in, first-out, which is stack-like behavior. This
flexibility is one of the main practical advantages of LinkedList.
Iterating a LinkedList
A LinkedList can be iterated using a for-each loop, an Iterator, or a
ListIterator. For sequential traversal, iteration is natural and efficient enough for many use
cases. For index-heavy loops, however, repeated calls to get(i) can be inefficient because each
call may require traversal.
for (Integer i : list) {
System.out.println(i);
}
Performance Complexity (Important)
Performance is the main reason LinkedList is compared with ArrayList. Access by
index is slower because nodes must be traversed. Adding or removing at the beginning or end is efficient.
Adding or removing in the middle can be efficient only after the location is reached; finding that location
still takes time.
| Operation | Time Complexity |
|---|---|
| get(index) | O(n) |
| add/remove (beginning) | O(1) |
| add/remove (middle) | O(n) |
| search | O(n) |
LinkedList vs ArrayList (Key Interview Comparison)
LinkedList and ArrayList both implement List, but their internal
structures are different. ArrayList is backed by a dynamic array, so it is strong for random
access and iteration. LinkedList is backed by nodes, so it is strong for operations at the ends
and useful for deque behavior.
| Aspect | LinkedList | ArrayList |
|---|---|---|
| Internal structure | Doubly linked list | Dynamic array |
| Random access | Slow | Fast |
| Insertion/deletion | Fast | Slow (middle) |
| Memory usage | Higher | Lower |
| Use case | Frequent modifications | Frequent reads |
The interview-safe answer is not that one is always better. ArrayList is usually preferred for
general list usage because most applications read and iterate more often than they insert in the middle.
LinkedList is useful when queue, deque, or frequent end modifications are central to the
requirement.
Thread Safety
LinkedList is not synchronized and is not thread-safe for concurrent modification. If multiple
threads modify the same list at the same time without coordination, the result can be inconsistent or unsafe.
This is similar to ArrayList. Thread safety must be handled intentionally when shared mutable
lists are involved.
Possible alternatives include wrapping the list with Collections.synchronizedList or using a
concurrent collection such as ConcurrentLinkedDeque when deque-style concurrent behavior is
required. The correct choice depends on whether the application needs list semantics, deque semantics, or
high-concurrency behavior.
When to Use LinkedList
Use LinkedList when the application benefits from deque behavior, frequent additions or removals
at the beginning or end, or stack and queue operations. It can be useful for processing tasks, buffering
values, maintaining ordered work items, or modeling workflows where elements are consumed from one end and
added to another.
It can also be useful when the code performs sequential traversal and structural modifications through an
iterator. The important point is that the access pattern should fit the linked structure. If most operations
are sequential and end-based, LinkedList can be a reasonable choice.
When NOT to Use LinkedList
Avoid LinkedList when the application frequently accesses elements by index. Repeated
get(i) calls can become expensive because each call may traverse nodes. For index-heavy
operations, ArrayList is usually a better fit.
LinkedList is also not ideal when memory is tight because each node stores extra references. For
large read-heavy lists, ArrayList is often more memory-efficient and faster in practice. In many
modern applications, ArrayList remains the default list implementation, while
LinkedList is chosen for specific deque or modification-heavy patterns.
Common Beginner Mistakes
A common beginner mistake is using LinkedList for random access as if it were an
ArrayList. Calling get(i) repeatedly inside a loop can be inefficient. Another
mistake is assuming LinkedList is always faster because insertion and deletion sound cheaper.
The cost of finding the insertion point still matters.
Beginners also ignore memory overhead. Each node stores extra references, so a linked list can use more memory
than an array-backed list. Another mistake is using LinkedList in shared multi-threaded code
without synchronization. Like most common collections, it requires care when multiple threads modify it.
LinkedList and Deque Behavior
One of the strongest reasons to use LinkedList is that it implements Deque.
Deque stands for double-ended queue. It allows elements to be added, removed, or inspected from
both the front and the back. This makes LinkedList more than a simple list implementation.
As a queue, LinkedList can use offer, poll, and peek.
As a stack-like structure, it can use push and pop. As a deque, it can use
addFirst, addLast, removeFirst, and removeLast. These
methods make intent clearer than using only generic list operations.
LinkedList and Memory Overhead
Memory overhead is a practical tradeoff of linked structures. In an ArrayList, elements are
stored in an internal array. In a LinkedList, each element is wrapped in a node object with
references to neighboring nodes. This means a linked list needs additional memory beyond the stored element
references.
For small lists, the difference may not matter. For very large lists, the overhead can become meaningful.
Memory usage can also affect cache locality and real-world performance. This is one reason
LinkedList is not automatically faster than ArrayList even when modifications are
involved.
LinkedList in Real Projects
In real projects, LinkedList is less common than ArrayList for ordinary list
storage, but it still has useful roles. It may appear in task queues, navigation history, ordered processing
pipelines, simple stack behavior, or algorithms that naturally operate from both ends of a sequence. Its
value is strongest when the code uses it as a deque rather than as a random-access list.
Test automation code may use linked lists less often, but the concept still matters in interviews and data
structure reasoning. A tester or automation engineer writing Java should understand why list choice affects
performance, even if most framework code uses ArrayList by default.
Interview Perspective
In interviews, a strong answer should define LinkedList as a doubly linked list implementation
that implements both List and Deque. It maintains insertion order, allows
duplicates, allows null values, and supports queue and stack-style operations. The answer should also explain
that random access is slower than ArrayList.
A stronger answer explains the tradeoff: LinkedList stores elements as nodes with previous and
next references, so it avoids element shifting but requires traversal for index access and uses extra memory.
It is useful for deque operations and specific modification patterns, not as a universal replacement for
ArrayList.
Queue Methods: offer, poll, and peek
When LinkedList is used as a queue, methods such as offer, poll, and
peek are preferred because they clearly describe queue behavior. offer adds an
element to the queue, poll retrieves and removes the head element, and peek
retrieves the head element without removing it. These names communicate intent better than using only generic
list methods.
The queue-style methods are also safer in some situations because they return special values instead of
throwing exceptions for certain empty-queue operations. For example, poll returns
null if the queue is empty, while methods such as removeFirst may throw an
exception. This distinction matters when designing robust processing logic.
In real code, choosing between exception-based methods and null-returning methods depends on expectation. If
an empty structure is normal, poll and peek can be convenient. If an empty structure
represents a programming error, exception-based methods may make the problem visible earlier.
Stack Behavior with LinkedList
LinkedList can also behave like a stack through push and pop. A stack
follows last-in, first-out behavior. The most recently added element is removed first. This pattern appears in
parsing, undo operations, expression evaluation, and certain algorithmic problems.
Although LinkedList supports stack behavior, modern Java often recommends ArrayDeque
for stack and deque operations because it is usually more efficient. Still, understanding
LinkedList stack behavior is valuable because it demonstrates how the class implements
Deque and why it is more flexible than a simple list.
LinkedList and Null Values
LinkedList allows null values, including multiple null values. Since it is a list, duplicates are
allowed, and null can appear at different positions. This behavior can be convenient when representing missing
or optional data, but it can also lead to errors if the code assumes every element is non-null.
Null values require careful handling during iteration, comparison, sorting, and method calls. If a list may contain null, code should check for null before calling methods on each element. In business applications, it is often better to prevent invalid nulls from entering the list rather than handling them repeatedly later.
Safe Removal While Iterating
Removing elements while iterating is a common operation and a common source of mistakes. If a developer uses a
for-each loop and directly calls list.remove, Java may throw a
ConcurrentModificationException. This is fail-fast behavior that protects the iteration from
unexpected structural changes.
The safer approach is to use an Iterator and call iterator.remove after identifying
the element that should be removed. Another approach is to collect values to remove in a separate collection
and remove them after traversal. Java also provides methods such as removeIf for predicate-based
removal.
This rule applies to many collection types, not only LinkedList. It is especially important in
filtering, cleanup, validation, and test data preparation logic where elements are commonly removed based on a
condition.
Converting LinkedList to Other Collections
A LinkedList can be converted into an ArrayList, array, set, or another collection
type when the required behavior changes. For example, if data was collected as a linked list for deque
processing but later needs fast index-based access, creating a new ArrayList from it may be
useful. If duplicates should be removed, converting to a Set may be appropriate.
Conversion should be intentional because the target collection may change behavior. A set removes duplicates.
A tree-based set sorts elements. An array fixes the size. An ArrayList provides faster random
access. The conversion should match the next operation the application needs to perform.
LinkedList and API Design
In method signatures, it is usually better to depend on an interface rather than the concrete
LinkedList class. If a method needs ordered list behavior, use List<T>. If a
method needs deque behavior, use Deque<T>. If a method only needs queue behavior, use
Queue<T>. This communicates the method's real requirement and gives callers flexibility.
Returning LinkedList directly from an API can overexpose implementation details. If callers only
need list behavior, returning List is clearer. If callers need both-end operations, returning
Deque is clearer. Good API design reveals behavior, not unnecessary internal choices.
LinkedList in Coding Interviews
LinkedList is a common interview topic because it connects Java collection usage with data
structure fundamentals. Interviewers may ask how it differs from ArrayList, why random access is
slower, what a doubly linked node contains, and how it supports queue and stack operations. They may also ask
when LinkedList is a better choice and when it is not.
A strong interview response avoids exaggerated claims. Do not say that LinkedList is always
faster for insertion and deletion. Say that it avoids array shifting, but locating the position may still
require traversal. Say that it is useful for deque operations and certain modification-heavy patterns, while
ArrayList is often better for read-heavy and index-heavy workloads.
LinkedList vs ArrayDeque
Since LinkedList implements Deque, it is often compared with
ArrayDeque. Both can be used for queue and stack-style operations, but their internal structures
differ. LinkedList uses nodes with previous and next references. ArrayDeque uses a
resizable array-based structure optimized for deque operations.
In many modern Java applications, ArrayDeque is preferred when the requirement is purely stack or
queue behavior. It usually has better memory locality and lower per-element overhead than
LinkedList. However, LinkedList remains useful to understand because it combines
List and Deque behavior in one class and clearly demonstrates linked-node data
structure concepts.
A practical interview answer can say that LinkedList is valid for deque operations, but
ArrayDeque is often the better modern choice for stack or queue implementations when list
behavior is not required. This shows awareness of both API capability and real-world performance tradeoffs.
ListIterator with LinkedList
ListIterator is useful with LinkedList because it supports bidirectional traversal.
A normal Iterator moves forward. A ListIterator can move forward with
next and backward with previous. It can also report indexes and support controlled
modifications during traversal.
This matters when a workflow needs to inspect neighboring elements, move backward after moving forward, or
modify a list while maintaining iterator consistency. Although not every application needs
ListIterator, knowing it demonstrates deeper understanding of the List interface and
the kinds of traversal patterns Java supports.
Performance Misconceptions
The biggest misconception about LinkedList is that it is always faster than
ArrayList for insertion and deletion. The more accurate statement is that once the target node is
known, changing links can be efficient. But if the program must first locate the position by index or search,
traversal still costs time. The total operation may not be faster than using an ArrayList.
Another misconception is that theoretical complexity always predicts real performance perfectly. In practice,
memory layout, CPU cache behavior, object allocation, garbage collection, and list size can affect results.
ArrayList often performs very well because its elements are stored in a compact array structure.
LinkedList has extra node objects and pointer traversal, which can be costly in real systems.
This does not mean LinkedList is useless. It means it should be selected for the right access
pattern. The best collection choice comes from the actual workload: how often the code reads by index, adds at
the ends, removes during traversal, searches, iterates, or needs deque behavior.
Designing with Interfaces
Because LinkedList implements several interfaces, developers should choose the reference type
carefully. If code declares LinkedList<String>, it exposes the concrete implementation.
If code declares List<String>, it says the code needs ordered list behavior. If code
declares Deque<String>, it says the code needs double-ended queue behavior.
This improves maintainability. A method that accepts Deque<Task> can work with
LinkedList today and ArrayDeque tomorrow. A method that accepts
List<Task> can work with ArrayList or LinkedList. Programming to
interfaces keeps the design flexible and communicates the intended operations more clearly.
LinkedList Examples
1. Creating a LinkedList
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("Java");
list.add("Selenium");
System.out.println(list);
}
}
Output
[Java, Selenium]
2. LinkedList Allows Duplicates
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(10);
list.add(10);
System.out.println(list);
}
}
Output
[10, 10]
3. Maintains Insertion Order
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(3);
list.add(1);
list.add(2);
System.out.println(list);
}
}
Output
[3, 1, 2]
4. Accessing Elements Using Index (Slower than ArrayList)
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
System.out.println(list.get(1));
}
}
Output
B
5. Adding Element at First Position
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(10);
list.addFirst(5);
System.out.println(list);
}
}
Output
[5, 10]
6. Adding Element at Last Position
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(10);
list.addLast(20);
System.out.println(list);
}
}
Output
[10, 20]
7. Removing First Element
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
list.removeFirst();
System.out.println(list);
}
}
Output
[B, C]
8. Removing Last Element
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
list.removeLast();
System.out.println(list);
}
}
Output
[A, B]
9. LinkedList as a Queue (FIFO)
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> queue = new LinkedList<>();
queue.offer(10);
queue.offer(20);
System.out.println(queue.poll());
System.out.println(queue);
}
}
Output
10
[20]
10. LinkedList as a Stack (LIFO)
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> stack = new LinkedList<>();
stack.push(10);
stack.push(20);
System.out.println(stack.pop());
System.out.println(stack);
}
}
Output
20
[10]
11. Iterating Using for-each Loop
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>(Arrays.asList("X", "Y", "Z"));
for (String s : list) {
System.out.println(s);
}
}
}
12. Iterating Using Iterator
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
13. Iterating Forward & Backward Using ListIterator
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
while (it.hasPrevious()) {
System.out.print(it.previous() + " ");
}
}
}
Output
A B C C B A
14. Removing Element While Iterating (Safe Way)
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
if (it.next() == 2) {
it.remove();
}
}
System.out.println(list);
}
}
Output
[1, 3]
15. LinkedList Size
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
System.out.println(list.size());
}
}
Output
3
16. Checking Element Existence
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>(Arrays.asList("Java", "API"));
System.out.println(list.contains("Java"));
}
}
Output
true
17. Converting LinkedList to ArrayList
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>(Arrays.asList("A", "B"));
ArrayList<String> arrayList = new ArrayList<>(linkedList);
System.out.println(arrayList);
}
}
Output
[A, B]
18. LinkedList vs ArrayList (Insertion Performance)
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(0, 100); // fast insertion
System.out.println(list);
}
}
Explanation
Insert/delete faster than ArrayList
Random access slower
19. LinkedList Is Not Thread-Safe
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("Not Thread Safe");
System.out.println(list);
}
}
Explanation
Use Collections.synchronizedList() if needed
20. Interview Summary: LinkedList
import java.util.*;
class Demo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("Doubly");
list.add("Linked");
list.add("List");
System.out.println(list);
}
}
Key Points
- Doubly linked list
- Faster insert/delete
- Slower random access
- Implements List and Deque
- Allows duplicates and maintains order
Interview-Ready Answers
Short Answer
LinkedList is a doubly linked list implementation of the List interface.
Detailed Answer
In Java, LinkedList implements both List and Deque interfaces. It stores elements as nodes with previous and next references, allowing fast insertion and deletion but slower random access compared to ArrayList.
Key Takeaway
Use LinkedList when modification speed matters more than access speed. Choose wisely based on access pattern and performance needs.