Skip to main content

Java LinkedList

Introduction​

The LinkedList class is a fundamental part of the Java Collections Framework that implements both the List and Deque interfaces. Unlike ArrayList, which is backed by a dynamic array, LinkedList is implemented as a doubly-linked list. Each element in a LinkedList maintains references to both the previous and next elements in the sequence, making it efficient for certain operations but less efficient for others.

In this tutorial, you'll learn:

  • What a LinkedList is and how it works
  • When to use LinkedList vs other collection types
  • Core operations and their time complexity
  • Practical examples and use cases
  • Best practices when working with LinkedList

Let's dive into this versatile data structure!

Understanding LinkedList​

What is a LinkedList?​

A LinkedList in Java is a data structure that stores elements in a sequence of nodes. Each node contains:

  • The actual element value
  • A reference to the previous node
  • A reference to the next node

This structure allows for efficient insertion and removal of elements from any position in the list.

LinkedList vs ArrayList​

Before diving deeper, let's compare LinkedList with its most common alternative, ArrayList:

OperationLinkedListArrayList
Access (get/set)O(n) - Linear timeO(1) - Constant time
Insert/Delete at beginningO(1) - Constant timeO(n) - Linear time
Insert/Delete at endO(1) - Constant timeO(1) amortized
Insert/Delete in middleO(n) to find + O(1) to changeO(n)
Memory overheadHigher (stores prev/next references)Lower

Getting Started with LinkedList​

Creating a LinkedList​

Let's start with the basics - creating a LinkedList:

java
import java.util.LinkedList;

// Create an empty LinkedList
LinkedList<String> names = new LinkedList<>();

// Create a LinkedList with initial elements
LinkedList<Integer> numbers = new LinkedList<>(List.of(1, 2, 3, 4, 5));

Basic Operations​

Here are some fundamental operations you can perform with a LinkedList:

java
import java.util.LinkedList;

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

// Adding elements
fruits.add("Apple"); // Adds to the end
fruits.add("Banana");
fruits.addFirst("Mango"); // Adds to the beginning
fruits.addLast("Orange"); // Adds to the end

System.out.println("LinkedList: " + fruits);

// Accessing elements
String first = fruits.getFirst();
String last = fruits.getLast();
String middle = fruits.get(1);

System.out.println("First: " + first);
System.out.println("Last: " + last);
System.out.println("Middle (index 1): " + middle);

// Removing elements
fruits.removeFirst(); // Removes from the beginning
fruits.removeLast(); // Removes from the end
fruits.remove(0); // Removes by index

System.out.println("After removal: " + fruits);

// Size
System.out.println("Size: " + fruits.size());
}
}

Output:

LinkedList: [Mango, Apple, Banana, Orange]
First: Mango
Last: Orange
Middle (index 1): Apple
After removal: [Banana]
Size: 1

Advanced LinkedList Operations​

Queue and Deque Operations​

Since LinkedList implements the Deque interface, it can be used as a queue or a stack:

java
import java.util.LinkedList;

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

// Using as a Queue (First-In-First-Out)
System.out.println("Using LinkedList as a Queue:");
list.offer("Task 1"); // Add to the tail
list.offer("Task 2");
list.offer("Task 3");

System.out.println(list);

// Process queue
while (!list.isEmpty()) {
System.out.println("Processing: " + list.poll()); // Remove from head
}

// Using as a Stack (Last-In-First-Out)
System.out.println("\nUsing LinkedList as a Stack:");
list.push("Page 1"); // Add to the head
list.push("Page 2");
list.push("Page 3");

System.out.println(list);

// Process stack
while (!list.isEmpty()) {
System.out.println("Processing: " + list.pop()); // Remove from head
}
}
}

Output:

Using LinkedList as a Queue:
[Task 1, Task 2, Task 3]
Processing: Task 1
Processing: Task 2
Processing: Task 3

Using LinkedList as a Stack:
[Page 3, Page 2, Page 1]
Processing: Page 3
Processing: Page 2
Processing: Page 1

Iterating Through a LinkedList​

There are multiple ways to iterate through a LinkedList:

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

public class LinkedListIteration {
public static void main(String[] args) {
LinkedList<String> countries = new LinkedList<>();
countries.add("USA");
countries.add("India");
countries.add("Japan");
countries.add("Brazil");

// Method 1: Enhanced for loop
System.out.println("Using enhanced for loop:");
for (String country : countries) {
System.out.println(country);
}

// Method 2: Iterator
System.out.println("\nUsing Iterator:");
Iterator<String> iterator = countries.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

// Method 3: ListIterator (allows backward iteration too)
System.out.println("\nUsing ListIterator (backwards):");
ListIterator<String> listIterator = countries.listIterator(countries.size());
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}

// Method 4: forEach with lambda (Java 8+)
System.out.println("\nUsing forEach method:");
countries.forEach(country -> System.out.println(country));
}
}

Output:

Using enhanced for loop:
USA
India
Japan
Brazil

Using Iterator:
USA
India
Japan
Brazil

Using ListIterator (backwards):
Brazil
Japan
India
USA

Using forEach method:
USA
India
Japan
Brazil

Practical Examples​

Example 1: Browser History Implementation​

A common use case for LinkedList is implementing a browser history functionality:

java
import java.util.LinkedList;

public class BrowserHistory {
private LinkedList<String> history = new LinkedList<>();
private int currentIndex = -1;

public void visit(String url) {
// Clear forward history when visiting a new page
while (history.size() > currentIndex + 1) {
history.removeLast();
}

history.add(url);
currentIndex++;
System.out.println("Visiting: " + url);
}

public void back() {
if (currentIndex > 0) {
currentIndex--;
System.out.println("Going back to: " + history.get(currentIndex));
} else {
System.out.println("Cannot go back - at oldest page");
}
}

public void forward() {
if (currentIndex < history.size() - 1) {
currentIndex++;
System.out.println("Going forward to: " + history.get(currentIndex));
} else {
System.out.println("Cannot go forward - at newest page");
}
}

public void showHistory() {
System.out.println("\nBrowsing History:");
for (int i = 0; i < history.size(); i++) {
if (i == currentIndex) {
System.out.println(i + ": " + history.get(i) + " (current)");
} else {
System.out.println(i + ": " + history.get(i));
}
}
System.out.println();
}

public static void main(String[] args) {
BrowserHistory browser = new BrowserHistory();

browser.visit("www.google.com");
browser.visit("www.wikipedia.org");
browser.visit("www.github.com");

browser.showHistory();

browser.back();
browser.back();
browser.showHistory();

browser.visit("www.stackoverflow.com");
browser.showHistory();

browser.forward();
}
}

Output:

Visiting: www.google.com
Visiting: www.wikipedia.org
Visiting: www.github.com

Browsing History:
0: www.google.com
1: www.wikipedia.org
2: www.github.com (current)

Going back to: www.wikipedia.org
Going back to: www.google.com

Browsing History:
0: www.google.com (current)
1: www.wikipedia.org
2: www.github.com

Visiting: www.stackoverflow.com

Browsing History:
0: www.google.com
1: www.stackoverflow.com (current)

Cannot go forward - at newest page

Example 2: Music Playlist Manager​

Another practical example is a music playlist manager:

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

public class MusicPlaylist {
private LinkedList<String> playlist = new LinkedList<>();
private ListIterator<String> iterator;
private String currentSong;

public MusicPlaylist() {
iterator = playlist.listIterator();
}

public void addSong(String song) {
playlist.add(song);
System.out.println("Added: " + song);
}

public void start() {
if (playlist.isEmpty()) {
System.out.println("Playlist is empty!");
return;
}

// Reset to beginning
iterator = playlist.listIterator();
if (iterator.hasNext()) {
currentSong = iterator.next();
System.out.println("Now playing: " + currentSong);
}
}

public void nextSong() {
if (iterator == null) {
System.out.println("Playlist hasn't been started yet. Call start() first.");
return;
}

if (iterator.hasNext()) {
currentSong = iterator.next();
System.out.println("Now playing: " + currentSong);
} else {
System.out.println("End of playlist reached.");
}
}

public void previousSong() {
if (iterator == null) {
System.out.println("Playlist hasn't been started yet. Call start() first.");
return;
}

// Go back twice because nextIndex points to the element after the current one
if (iterator.hasPrevious()) {
iterator.previous();
if (iterator.hasPrevious()) {
currentSong = iterator.previous();
// Move forward one to maintain position
iterator.next();
System.out.println("Now playing: " + currentSong);
} else {
// At the beginning, just move forward to maintain position
if (iterator.hasNext()) {
iterator.next();
}
System.out.println("Beginning of playlist reached.");
}
} else {
System.out.println("Beginning of playlist reached.");
}
}

public void showPlaylist() {
System.out.println("\nPlaylist:");
int index = 0;
for (String song : playlist) {
if (song.equals(currentSong)) {
System.out.println(index + ": " + song + " ← PLAYING");
} else {
System.out.println(index + ": " + song);
}
index++;
}
System.out.println();
}

public static void main(String[] args) {
MusicPlaylist player = new MusicPlaylist();

player.addSong("Bohemian Rhapsody - Queen");
player.addSong("Imagine - John Lennon");
player.addSong("Shape of You - Ed Sheeran");
player.addSong("Billie Jean - Michael Jackson");
player.addSong("Hotel California - Eagles");

player.start();
player.showPlaylist();

player.nextSong();
player.nextSong();
player.showPlaylist();

player.previousSong();
player.showPlaylist();
}
}

Output:

Added: Bohemian Rhapsody - Queen
Added: Imagine - John Lennon
Added: Shape of You - Ed Sheeran
Added: Billie Jean - Michael Jackson
Added: Hotel California - Eagles
Now playing: Bohemian Rhapsody - Queen

Playlist:
0: Bohemian Rhapsody - Queen ← PLAYING
1: Imagine - John Lennon
2: Shape of You - Ed Sheeran
3: Billie Jean - Michael Jackson
4: Hotel California - Eagles

Now playing: Imagine - John Lennon
Now playing: Shape of You - Ed Sheeran

Playlist:
0: Bohemian Rhapsody - Queen
1: Imagine - John Lennon
2: Shape of You - Ed Sheeran ← PLAYING
3: Billie Jean - Michael Jackson
4: Hotel California - Eagles

Now playing: Imagine - John Lennon

Playlist:
0: Bohemian Rhapsody - Queen
1: Imagine - John Lennon ← PLAYING
2: Shape of You - Ed Sheeran
3: Billie Jean - Michael Jackson
4: Hotel California - Eagles

Best Practices​

When working with LinkedList, keep these tips in mind:

  1. Choose the right collection: Use LinkedList when you need frequent insertions/deletions at both ends or in the middle, and don't need random access.

  2. Avoid random access: Don't use get(index) frequently as it's an O(n) operation. Traverse using iterators instead.

  3. Use specialized methods: Use the specialized methods like addFirst(), addLast(), removeFirst(), removeLast() instead of generic add(index, element) when applicable.

  4. Consider memory usage: LinkedList uses more memory than ArrayList due to the overhead of storing node references.

  5. Be careful with large lists: Due to its memory overhead and lack of locality of reference, a very large LinkedList can be less efficient than other collections.

Summary​

In this tutorial, you've learned:

  • The structure and characteristics of Java's LinkedList implementation
  • Basic operations like adding, accessing, and removing elements
  • Advanced operations using queue and deque functionality
  • Different ways to iterate through a LinkedList
  • Real-world applications with practical examples
  • Best practices for efficient use of LinkedList

LinkedList is a versatile data structure that's particularly useful when you need fast insertions and deletions at any position within a list. While it may not be as efficient as ArrayList for random access operations, its flexibility makes it an important tool in any Java programmer's toolkit.

Additional Resources​

Practice Exercises​

  1. Implement a text editor's undo/redo functionality using LinkedList.
  2. Create a simulation of a customer service queue using LinkedList.
  3. Build a card game where players can draw and discard cards, using LinkedList for the deck and discard pile.
  4. Implement a custom LinkedList using your own Node class to better understand how it works internally.
  5. Create a program that removes duplicates from a LinkedList while maintaining the original order.


If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)