The Empty List Is Already Handled
Try This First
You have a singly linked list with a head field (no dummy head) and a size field. The list is empty: head == null, size == 0. You call toString(), which starts with:
Node<T> cur = head;
while (cur != null) {
// build the string
cur = cur.next;
}
What does the loop do?
Reveal
cur is assigned null (the value of head). The condition cur != null evaluates to false immediately. The loop body never runs. The traversal contributes nothing to the output, which is exactly right for an empty list.
What You Need To Walk In With
head == nullis the defining condition of an empty list in the no-dummy-head design.- The canonical traversal
while (cur != null)is “self-protecting”: whencurstarts asnull, the condition is immediately false and the body never executes. - You only need an explicit
if (head == null)check when the operation has a different return value or behavior for the empty case (for example, returning the string"Empty List"or throwingNoSuchElementException). - Reading
sizeon an empty list returns0directly from the field; no traversal is involved. - The alternative idiom
while (cur.next != null)is NOT self-protecting: calling.nexton a null reference throws aNullPointerExceptionbefore the body even runs.
How It Works
The no-dummy-head list stores a head reference and a size field:
public class LinkedList<T> {
private Node<T> head; // null when the list is empty
private int size; // 0 when the list is empty
// ...
}
A traversal starts by copying head into a local cursor:
Node<T> cur = head; // cur == null if the list is empty
while (cur != null) { // condition is false immediately; body runs 0 times
// ... process cur.data ...
cur = cur.next;
}
The condition acts as a natural guard. No element is ever touched when the list is empty.
When the operation needs to distinguish “empty” from “non-empty” semantics, the explicit check goes at the top, before the loop:
public String toString() {
if (head == null) return "Empty List"; // different return value for empty
StringBuilder sb = new StringBuilder();
Node<T> cur = head;
while (cur != null) {
sb.append(cur.data).append(" ");
cur = cur.next;
}
return sb.toString().trim();
}
Here the guard is not redundant because it controls what the method returns, not whether the loop is safe to run.
Empty list:
head --> null
cur = null
while (null != null) --> false, loop exits immediately
Worked Example: Predict, Then Check
You call toString() on a list where head == null. The method body is:
public String toString() {
if (head == null) return "Empty List";
Node<T> cur = head;
while (cur != null) {
sb.append(cur.data).append(" ");
cur = cur.next;
}
return sb.toString().trim();
}
What string does the method return, and does the while loop ever execute?
Reveal
The method returns "Empty List" and the while loop never executes. Because head == null, the explicit if check at the top fires immediately and returns before reaching the loop. The traversal contributes nothing. (Source: S20.Lab5-LL_NDH.LinkedList.toString; Liang 10e Ch 24)
A Common Mistake
A common mistake is writing while (cur.next != null) instead of while (cur != null). On a non-empty list, this idiom stops one node too early. On an empty list, the consequence is worse: cur is null, so cur.next immediately throws a NullPointerException before the loop body ever runs. The fix is to always use the canonical form while (cur != null), which tests the cursor itself rather than the field of something that might not exist. (Source: Liang 10e Ch 24)
Go Deeper (optional)
Once you are comfortable with this pattern, notice that the self-protecting property of while (cur != null) also means the loop runs in O(n) time where n is the number of nodes, and O(1) when the list is empty. If your class adds a dummy head node in a later course or project, the empty-list check changes shape entirely: the list is never truly empty because the dummy node is always present. Understanding the no-dummy-head form first makes that contrast meaningful.
Check Yourself
Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.
Check your understanding
A no-dummy-head LinkedList<T> has head == null. What happens when you execute: Node<T> cur = head; while (cur != null) { ... }
When is an explicit if (head == null) check necessary before a traversal loop?
Consider: Node<T> cur = head; while (cur.next != null) { cur = cur.next; } where head == null. What happens?
You call size() on an empty no-dummy-head list that stores size as a field. What does it return and why?
A toString() method begins with if (head == null) return “Empty List”; then sets cur = head and enters while (cur != null). The list has head == null. Which line executes last before the method returns?