import java.util.LinkedList;
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
names.add("Priya");
System.out.println(names);Java LinkedList: Doubly-Linked Lists, Queue Operations, and Use Cases
Direct Answer & Core Concept
LinkedList is a Java collection class that implements the List and Deque interfaces. Unlike ArrayList, which is based on a resizable array, LinkedList stores elements as linked nodes.
Each node keeps a reference to the element and links to neighboring nodes. In Java, LinkedList is implemented as a doubly-linked list, meaning each node is connected to both the previous and next node.
Because it implements Deque, LinkedList can also be used for queue, double-ended queue, and stack-like operations.
Detailed Explanation & Mechanics
What Is LinkedList in Java?
LinkedList is a class from the java.util package.
Basic example:
Output:
[Amit, Rahul, Priya]A LinkedList can dynamically grow as elements are added.
Importing LinkedList
You need to import the class before using it:
import java.util.LinkedList;You can also use:
import java.util.*;However, importing the specific class makes dependencies clearer.
Creating a LinkedList
The basic syntax is:
LinkedList<Type> list = new LinkedList<>();For example:
LinkedList<String> cities = new LinkedList<>();An integer list can be created with:
LinkedList<Integer> numbers = new LinkedList<>();Remember that Java collections use wrapper classes rather than primitive types such as int.
How LinkedList Works Internally
The key idea behind LinkedList is its node structure.
Conceptually, a doubly-linked list looks like this:
null↓[Previous | A | Next] ⇄[Previous | B | Next] ⇄[Previous | C | Next]↓nullEach node contains:
A reference to the stored element
A reference to the previous node
A reference to the next node
This allows nodes to be connected in both directions.
Java's LinkedList maintains references to the first and last nodes, which helps with operations at both ends.
Doubly-Linked List Explained
A singly-linked list normally has one direction:
A → B → C → DA doubly-linked list has two directions:
A ⇄ B ⇄ C ⇄ DThe ability to move forward and backward through the nodes is useful for certain insertion, removal, and deque operations.
However, this does not mean every LinkedList operation is automatically faster than ArrayList.
Adding Elements to LinkedList
The simplest operation is:
add()Example:
LinkedList<String> fruits = new LinkedList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Mango");
System.out.println(fruits);Output:
[Apple, Banana, Mango]Elements are added to the end of the list.
Adding an Element at a Specific Position
You can insert an element at an index:
fruits.add(1, "Orange");The resulting list becomes:
[Apple, Orange, Banana, Mango]The important performance detail is that LinkedList still needs to locate the requested position before inserting there.
Therefore, insertion at an arbitrary index is not automatically O(1).
Adding Elements at the Beginning
LinkedList provides:
addFirst()Example:
LinkedList<String> names = new LinkedList<>();
names.add("Rahul");
names.add("Priya");
names.addFirst("Amit");
System.out.println(names);Output:
[Amit, Rahul, Priya]This operation can be performed efficiently because the list maintains a reference to its first node.
Adding Elements at the End
You can use:
addLast()Example:
LinkedList<String> names = new LinkedList<>();
names.addLast("Amit");
names.addLast("Rahul");
names.addLast("Priya");
System.out.println(names);Output:
[Amit, Rahul, Priya]add() also adds to the end for a normal LinkedList.
Accessing Elements with get()
You can access an element using:
get(index)Example:
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
names.add("Priya");
System.out.println(names.get(1));Output:
RahulHowever, unlike ArrayList, indexed access in a LinkedList is typically O(n).
The list must traverse nodes to reach the requested position.
Why LinkedList Is Slower for Random Access
Consider:
list.get(5000);An array-backed ArrayList can typically calculate the location directly.
A linked list has to follow node references until it reaches the required position.
Conceptually:
Start↓Node 1↓Node 2↓Node 3↓...↓Node 5000This makes repeated random indexed access inefficient compared with ArrayList.
Updating an Element
You can use:
set(index, value)Example:
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
names.add("Priya");
names.set(1, "Rohan");
System.out.println(names);Output:
[Amit, Rohan, Priya]The list size does not change.
Removing Elements
You can remove an element by index:
names.remove(1);Or remove an object:
names.remove("Amit");Example:
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
names.add("Priya");
names.remove("Rahul");
System.out.println(names);Output:
[Amit, Priya]Removing the First Element
Use:
removeFirst()Example:
LinkedList<String> queue = new LinkedList<>();
queue.add("A");
queue.add("B");
queue.add("C");
queue.removeFirst();
System.out.println(queue);Output:
[B, C]This is useful for queue-style processing.
Removing the Last Element
Use:
removeLast()Example:
LinkedList<String> list = new LinkedList<>();
list.add("A");
list.add("B");
list.add("C");
list.removeLast();
System.out.println(list);Output:
[A, B]ArrayList vs LinkedList
Both classes implement List, but their underlying structures are different.
| Feature | ArrayList | LinkedList |
|---|---|---|
| Main structure | Resizable array | Doubly-linked nodes |
Indexed get() | Typically O(1) | Typically O(n) |
| Add at end | Amortized O(1) | O(1) |
| Add at beginning | O(n) | O(1) |
| Remove at beginning | O(n) | O(1) |
| Random access | Excellent | Poor |
| Memory overhead | Generally lower | Generally higher |
Implements Deque | No | Yes |
| Typical list default | Often | Less often |
The complexity of insertion/removal in a linked list needs an important qualification: once the correct node is already known, changing the links can be O(1). Finding an arbitrary position can take O(n).
LinkedList as a Queue
One of the useful features of LinkedList is that it implements Queue.
A queue normally follows FIFO:
First In → First OutFor example:
Customer A → Customer B → Customer CCustomer A is processed first.
You can use:
Queue<String> queue = new LinkedList<>();Example:
import java.util.LinkedList;
import java.util.Queue;
Queue<String> queue = new LinkedList<>();
queue.offer("A");
queue.offer("B");
queue.offer("C");
System.out.println(queue);Output:
[A, B, C]Queue offer(), peek(), and poll()
Three important queue operations are:
offer()
Adds an element to the queue.
queue.offer("D");peek()
Returns the front element without removing it.
System.out.println(queue.peek());poll()
Returns and removes the front element.
System.out.println(queue.poll());Example:
Queue<String> queue = new LinkedList<>();
queue.offer("A");
queue.offer("B");
queue.offer("C");
System.out.println(queue.peek());
System.out.println(queue.poll());
System.out.println(queue);Output:
AA[B, C]Queue Example: Customer Processing
A queue can represent customers waiting for service.
Queue<String> customers = new LinkedList<>();
customers.offer("Customer 1");
customers.offer("Customer 2");
customers.offer("Customer 3");
while (!customers.isEmpty()) {
String customer = customers.poll();
System.out.println("Serving: " + customer);
}Output:
Serving: Customer 1Serving: Customer 2Serving: Customer 3This demonstrates the FIFO principle.
LinkedList as a Deque
Because LinkedList implements Deque, you can add and remove elements from both ends.
Deque<String> deque = new LinkedList<>();
deque.addFirst("B");
deque.addLast("C");
deque.addFirst("A");
System.out.println(deque);Output:
[A, B, C]A deque is useful when both the front and back of a collection need to be modified.
Important Deque Methods
Some commonly used methods include:
| Method | Purpose |
|---|---|
addFirst() | Add at front |
addLast() | Add at end |
removeFirst() | Remove front |
removeLast() | Remove end |
peekFirst() | View front |
peekLast() | View end |
offerFirst() | Offer at front |
offerLast() | Offer at end |
pollFirst() | Remove and return front |
pollLast() | Remove and return end |
Example:
Deque<Integer> numbers = new LinkedList<>();
numbers.addFirst(20);
numbers.addLast(30);
numbers.addFirst(10);
System.out.println(numbers);Output:
[10, 20, 30]LinkedList as a Stack
Because Deque supports operations at both ends, a LinkedList can also be used for stack-like behavior.
However, for a normal stack/deque workload, ArrayDeque is generally a better modern choice.
Example:
Deque<String> stack = new LinkedList<>();
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println(stack.pop());Output:
CThis follows:
LIFOLast In → First OutArrayDeque vs LinkedList
Although LinkedList implements Deque, it is not always the best deque implementation.
For many ordinary queue, deque, and stack use cases, ArrayDeque is often preferred because its array-backed structure generally has lower memory overhead and better cache locality.
Example:
Deque<String> queue = new ArrayDeque<>();Use LinkedList when its list behavior is useful as well, rather than choosing it simply because it supports queue operations.
LinkedList Performance
Typical complexity characteristics are:
| Operation | Typical Complexity |
|---|---|
get(index) | O(n) |
set(index, value) | O(n) |
| Add at beginning | O(1) |
| Add at end | O(1) |
| Remove first | O(1) |
| Remove last | O(1) |
| Search | O(n) |
| Insert at arbitrary index | O(n) |
| Remove at arbitrary index | O(n) |
The O(n) complexity for indexed insertion/removal includes the cost of locating the position.
Once a node is already known, changing neighboring links can be performed in constant time.
Why LinkedList Uses More Memory
Each linked-list node needs more than just the stored value.
Conceptually, each node contains:
+-----------------------------+| Element || Previous node reference || Next node reference |+-----------------------------+Therefore, a LinkedList has additional per-element memory overhead compared with an array-backed list.
For large collections, this difference can matter.
LinkedList and Null Values
LinkedList allows null elements.
Example:
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add(null);
names.add("Rahul");
System.out.println(names);Output:
[Amit, null, Rahul]Whether null should be used depends on the application's design.
LinkedList and Duplicate Elements
Duplicates are allowed.
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
names.add("Amit");
System.out.println(names);Output:
[Amit, Rahul, Amit]If you need unique values, a Set may be more appropriate.
Checking the Size
Use:
size()Example:
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
System.out.println(names.size());Output:
2Checking Whether the List Is Empty
Use:
isEmpty()Example:
LinkedList<String> names = new LinkedList<>();
System.out.println(names.isEmpty());Output:
trueAfter adding an element:
names.add("Amit");
System.out.println(names.isEmpty());Output:
falseSearching in LinkedList
You can use:
contains()Example:
LinkedList<String> languages = new LinkedList<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
System.out.println(languages.contains("Java"));Output:
trueYou can also use:
indexOf()and:
lastIndexOf()Search operations are typically O(n).
Iterating Through LinkedList
An enhanced for loop is simple:
for (String name : names) {
System.out.println(name);
}You can also use an iterator:
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}For bidirectional traversal, LinkedList also supports ListIterator.
ListIterator with LinkedList
ListIterator can move forward and backward.
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
names.add("Priya");
ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}while (iterator.hasPrevious()) {
System.out.println(iterator.previous());
}This can be useful when an application needs list traversal in both directions.
Clearing a LinkedList
The clear() method removes all elements.
LinkedList<String> names = new LinkedList<>();
names.add("Amit");
names.add("Rahul");
names.clear();
System.out.println(names);Output:
[]LinkedList with Custom Objects
A LinkedList can store objects of your own classes.
class Product {
String name;
double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
}Create the list:
LinkedList<Product> products = new LinkedList<>();
products.add(new Product("Laptop", 60000));
products.add(new Product("Mouse", 1000));Iterate over it:
for (Product product : products) {
System.out.println(product.name + " - " + product.price);
}This pattern is common in business applications.
Programming to the Interface
Instead of:
LinkedList<String> names = new LinkedList<>();you can use:
List<String> names = new LinkedList<>();when you need list behavior.
If the primary purpose is queue/deque processing:
Deque<String> queue = new LinkedList<>();This communicates the intended behavior more clearly.
It also allows the implementation to be changed later with less impact:
Deque<String> queue = new ArrayDeque<>();Real-World Use Cases for LinkedList
LinkedList can be useful when an application naturally needs linked-list behavior or when the same structure needs both List and Deque capabilities.
Potential examples include:
1. Frequent Operations at Both Ends
Deque<String> tasks = new LinkedList<>();
tasks.addFirst("Urgent Task");
tasks.addLast("Normal Task");2. Bidirectional List Processing
A ListIterator can traverse the collection forward and backward.
3. Small Deque-Based Structures
When you specifically need a deque and also want LinkedList semantics, it can be suitable.
4. Teaching Linked Data Structures
LinkedList is useful for learning how nodes, references, insertion, and removal work.
When LinkedList Is Not a Good Choice
Avoid choosing LinkedList simply because insertion or removal sounds faster.
For example, if your application frequently does:
list.get(5000);
list.get(9000);
list.get(12000);an ArrayList is generally better suited to indexed access.
Likewise, if you simply need a queue or stack, consider:
ArrayDequebefore choosing LinkedList.
The workload should determine the collection.
Common LinkedList Mistakes
Mistake 1: Assuming LinkedList Has Fast Random Access
This:
list.get(50000);is not a constant-time operation.
The list must locate the appropriate node.
Mistake 2: Assuming LinkedList Is Always Faster Than ArrayList
The underlying workload matters.
ArrayList can be faster for many real-world workloads because indexed access is constant-time and its contiguous storage can provide good cache locality.
Mistake 3: Using LinkedList as a Queue Without Knowing Queue Methods
Instead of manually managing indexes, use the queue API:
Queue<String> queue = new LinkedList<>();
queue.offer("A");
queue.offer("B");
String item = queue.poll();This expresses the FIFO behavior directly.
Mistake 4: Using LinkedList for Every Insertion Problem
Even if insertion/removal can be efficient after reaching a node, locating an arbitrary position can still take O(n).
The actual operation pattern matters.
Complete LinkedList Example
The following example demonstrates list and deque operations:
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
LinkedList<String> names = new LinkedList<>();
// Add elements
names.add("Rahul");
names.add("Priya");
names.add("Amit");
// Add at beginning
names.addFirst("Rohan");
// Add at end
names.addLast("Sneha");
System.out.println("List: " + names);
// Access
System.out.println("First: " + names.getFirst());
System.out.println("Last: " + names.getLast());
// Remove first
names.removeFirst();
// Remove last
names.removeLast();
System.out.println("After removal: " + names);
// Search
System.out.println("Contains Amit: " + names.contains("Amit"));
// Size
System.out.println("Size: " + names.size());
}
}Possible output:
List:[Rohan, Rahul, Priya, Amit, Sneha]First: RohanLast: SnehaAfter removal:[Rahul, Priya, Amit]Contains Amit: trueSize: 3Queue Example Using LinkedList
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.offer("Task 1");
queue.offer("Task 2");
queue.offer("Task 3");
while (!queue.isEmpty()) {
String task = queue.poll();
System.out.println("Processing: " + task);
}
}
}Output:
Processing: Task 1Processing: Task 2Processing: Task 3This demonstrates FIFO processing.
LinkedList Quick Revision
| Concept | Key Point |
|---|---|
| Package | java.util |
| Main structure | Doubly-linked nodes |
| Interfaces | List, Deque, Queue indirectly |
| Duplicate values | Allowed |
null | Allowed |
| Indexed access | Typically O(n) |
| Add first | O(1) |
| Add last | O(1) |
| Remove first | O(1) |
| Remove last | O(1) |
| Search | O(n) |
| Random access | Poor compared with ArrayList |
| Memory overhead | Higher than array-backed lists |
| Queue support | Yes |
| Deque support | Yes |
Frequently Asked Questions
What is LinkedList in Java?
LinkedList is a collection class in the java.util package that implements a doubly-linked list and also provides List and Deque functionality.
Is Java LinkedList a doubly-linked list?
Yes. Java's LinkedList is implemented as a doubly-linked list.
What is the difference between ArrayList and LinkedList?
ArrayList uses a resizable array and provides fast indexed access. LinkedList uses linked nodes and provides efficient operations at its ends, but indexed access is typically O(n).
Is LinkedList faster than ArrayList?
Not generally. The answer depends on the workload. ArrayList is often preferable for indexed access and many general-purpose list workloads.
Can LinkedList be used as a queue?
Yes. It implements Deque, which extends Queue.
Example:
Queue<String> queue = new LinkedList<>();Can LinkedList be used as a stack?
Yes, it can provide stack-like operations through Deque. However, ArrayDeque is generally preferred for ordinary stack/deque use cases.
Does LinkedList allow duplicate values?
Yes. Multiple equal elements can be stored.
Does LinkedList allow null?
Yes. A LinkedList can contain null.
Why does LinkedList use more memory?
Each node needs references to the previous and next nodes in addition to the stored element.
What is the time complexity of get() in LinkedList?
Indexed get() is typically O(n), because the list may need to traverse nodes to reach the requested position.
When should I use LinkedList?
Use it when its deque/list behavior fits the workload, particularly when operations at the ends are important. For many ordinary lists, ArrayList is often a better default.
Graphics and Design Ideas
1. Doubly-Linked List Diagram
Show the structure visually:
null
↓
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ prev | A | next │ ⇄ │ prev | B | next │ ⇄ │ prev | C | next │
└──────────────┘ └──────────────┘ └──────────────┘
↑ ↑
First LastUse arrows in both directions to make the concept of a doubly-linked list clear.
2. ArrayList vs LinkedList Graphic
Create a side-by-side comparison:
ArrayList[ A][ B][ C][ D][ E] Array Storage Fast Index AccessLinkedList[A] ⇄[B] ⇄[C] ⇄[D] ⇄[E] Linked Nodes Sequential Traversal3. FIFO Queue Graphic
Show:
FIFOIncoming↓[A] →[B] →[C] →[D] ↑Front RearThen show poll() removing A.
4. Deque Graphic
Illustrate operations from both ends:
addFirst()
↓
┌────┬────┬────┬────┐
│ A │ B │ C │ D │
└────┴────┴────┴────┘
↑
addLast()
removeFirst() ← → removeLast()5. Performance Comparison Chart
Create a visual table:
Operation ArrayList LinkedListget(index) O(1) O(n)add(end) O(1)* O(1)add(beginning) O(n) O(1)remove(beginning) O(n) O(1)search O(n) O(n)Add a note:
* Amortized complexity.
6. Real-World Use Case Graphic
Create four cards:
Queue
Customer service processing
Deque
Add/remove from both ends
Bidirectional Traversal
Forward and backward navigation
Linked Data Structure Learning
Understanding nodes and references
Runnable Code Example: Java LinkedList
Java 17+import java.util.LinkedList;
import java.util.Deque;
public class LinkedListDemo {
public static void main(String[] args) {
Deque<String> queue = new LinkedList<>();
queue.offer("Task 1");
queue.offer("Task 2");
queue.offer("Task 3");
System.out.println("Polled from queue: " + queue.poll());
// Task 1
System.out.println("Remaining queue: " + queue);
}
}