Java Linked Lists (from scratch)
A linked list is a data structure made of nodes, where each node stores a value and a reference to the next node. Instead of storing items side by side like an array, a linked list connects items one at a time.
Java already has java.util.LinkedList, but building a small one from scratch helps you understand how node-based data structures work.
The Node Idea
Each node needs two pieces of information: the value it stores and a reference to the next node. The first node is usually called head. If you also store a tail reference, adding to the end can be direct because the list remembers its last node.
headpoints to the first node.tailpoints to the last node.nextpoints from one node to the following node.nullmeans there is no next node.
Build A Simple Linked List
This example creates a small linked list that stores int values. It supports adding to the front, adding to the back, removing the first node, searching, checking the size, and printing the list.
public class Main {
static class Node {
int value;
Node next;
Node(int value) {
this.value = value;
}
}
static class IntLinkedList {
private Node head;
private Node tail;
private int size;
public void addFirst(int value) {
Node node = new Node(value);
if (head == null) {
head = node;
tail = node;
} else {
node.next = head;
head = node;
}
size++;
}
public void addLast(int value) {
Node node = new Node(value);
if (tail == null) {
head = node;
tail = node;
} else {
tail.next = node;
tail = node;
}
size++;
}
public boolean removeFirst() {
if (head == null) {
return false;
}
head = head.next;
if (head == null) {
tail = null;
}
size--;
return true;
}
public boolean contains(int value) {
Node current = head;
while (current != null) {
if (current.value == value) {
return true;
}
current = current.next;
}
return false;
}
public int size() {
return size;
}
public void print() {
Node current = head;
while (current != null) {
System.out.print(current.value);
if (current.next != null) {
System.out.print(" -> ");
}
current = current.next;
}
System.out.println();
}
}
public static void main(String[] args) {
IntLinkedList numbers = new IntLinkedList();
numbers.addLast(10);
numbers.addLast(20);
numbers.addFirst(5);
numbers.print();
System.out.println("Contains 20: " + numbers.contains(20));
numbers.removeFirst();
numbers.print();
System.out.println("Size: " + numbers.size());
}
}
Output:
5 -> 10 -> 20
Contains 20: true
10 -> 20
Size: 2
How It Works
addFirst() creates a new node, points it at the old head, and then makes the new node the head. If the list was empty, both head and tail point to the same new node.
addLast() uses tail.next to attach the new node after the old last node, then moves tail forward. This is why keeping a tail reference is useful.
removeFirst() moves head to head.next. If that makes head become null, the list is empty, so tail must also become null.
Traversal
Traversal means walking through the list one node at a time. The contains() and print() methods both start at head, use a current variable, and repeatedly move to current.next until they reach null.
Performance Basics
| Operation | With This Design | Why |
|---|---|---|
| Add first | O(1) |
Only changes head |
| Add last | O(1) |
Uses stored tail |
| Search | O(n) |
May visit every node |
| Access by index | O(n) |
Must walk from head |
Common Mistakes
- Forgetting to update
tailwhen the list becomes empty. - Losing the rest of the list by changing
headbefore saving the needed reference. - Using a linked list when frequent index access would make an array or
ArrayListsimpler.
Takeaway: a linked list is a chain of nodes, and most operations work by carefully changing which node each reference points to.
