Node fundamentals
The building block of every linked structure: a node holding a value and a reference to the next node. This lesson covers value and next, head and tail, the singly linked list, null termination and reference identity.
Learning goals
- Define a node class and build a small list by hand
- Explain head, tail and null termination
- Distinguish a node from a reference to it and == from equals
Prerequisites
Value and next pointer
A linked list node stores one value and one reference to the next node. In Java the reference is just a field of the node's own type, and it is null when there is no next node. Nodes are separate objects on the heap, so a list needs no contiguous block of memory.
Each node costs an object header plus the value and the reference: about 24 bytes for an int value on a 64-bit JVM with compressed references.
1class Node {2int val;3Node next; // reference to the next node, or null4Node(int val) { this.val = val; }5}6Node a = new Node(10), b = new Node(20), c = new Node(30);7a.next = b; b.next = c; // a -> b -> c -> null
Head and tail
The head is the first node and the only entry point: lose the head reference and the whole list is unreachable. The tail is the last node, the one whose next is null. A singly linked list usually stores only the head; keeping a separate tail reference makes appending O(1).
Without a tail pointer, reaching the end means walking the whole list.
1class LinkedList {2Node head, tail;3int size;4void addLast(int v) { // O(1) thanks to the tail reference5Node n = new Node(v);6if (head == null) head = tail = n;7else { tail.next = n; tail = n; }8size++;9}10}
Singly linked list
A singly linked list is a chain of nodes where each node points only forward. Reading the k-th element needs k hops (O(k)), while inserting or deleting right after a node you already hold is O(1) with no shifting. It cannot move backwards, so anything that needs the previous node must keep it while walking.
The animation contrasts reading element k in an array with walking k links.
| Operation | Array | Singly linked list |
|---|---|---|
| Access by index | O(1) | O(n) |
| Insert/delete at the head | O(n) | O(1) |
| Insert/delete after a known node | O(n) | O(1) |
| Insert at the tail | O(1) amortised | O(1) with a tail pointer, else O(n) |
| Memory per element | 4 bytes (int) | about 24 bytes |
Reading element k: array vs linked list
Interactive · edit the inputarray memory (value, address)
The 7 values sit in consecutive memory cells, 4 bytes apart, starting at address 1000.
1static int getFromArray(int[] arr, int k) {2return arr[k]; // address = base + k * 4: one memory read3}45static int getFromList(Node head, int k) {6Node cur = head;7for (int i = 0; i < k; i++) {8cur = cur.next; // pointer chase: one read per hop9}10return cur.value;11}
Live state
k
5
base
1000
Execution trace (dry run table)Show 3 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | k=5 base=1000 | The 7 values sit in consecutive memory cells, 4 bytes apart, starting at address 1000. |
| 2 | L2 | k=5 address=1020 | Address = 1000 + 5 × 4 = 1020. One multiplication and one addition give the location, no matter how large k is. |
| 3 | L2 | return=8 | return 8 with a single memory read: O(1) random access. |
Null termination
The end of a list is marked by next == null. Loops walk until the current node is null (visiting every node) or until cur.next is null (stopping at the last node), and choosing the wrong one is a classic off-by-one. Always guard against an empty list (head == null), and never call a method on a possibly-null reference.
A cycle breaks this contract: the loop would never see null.
1static int length(Node head) {2int n = 0;3for (Node cur = head; cur != null; cur = cur.next) n++; // stops after the last node4return n;5}6static Node last(Node head) {7if (head == null) return null;8Node cur = head;9while (cur.next != null) cur = cur.next; // stops AT the last node10return cur;11}
Reference identity
Variables of type Node hold references. a == b is true only if both refer to the same node object; two nodes with equal values are different nodes. Assigning Node cur = head copies the reference, not the node, so moving cur never changes the list, while writing cur.next = x changes the shared node everyone sees.
This is why “moving a pointer” and “changing a link” are different operations.
1Node a = new Node(5), b = new Node(5);2boolean sameNode = (a == b); // false: two different objects with equal values3Node alias = a;4alias.val = 9; // a.val is now 9 too5Node cur = a; cur = cur.next; // moves the local reference only; the list is unchanged
Practice ladder
Now try it yourself
Easy
- Build the list 1 → 2 → 3 by hand
- What is returned by length(null)?
Medium
- Write last(head) and explain the difference between cur != null and cur.next != null loops
- Why does losing head lose the whole list?
Advanced
- Implement a LinkedList class with head, tail and size supporting addFirst, addLast and removeFirst in O(1)
Key takeaways
- A node is a value plus a reference; the list is reached through head.
- Null marks the end; guard the empty list.
- == compares references, and copying a reference never copies a node.