← Linked lists CS Skill Tree

Adding before a value

The lesson behind it is Adding a node, every case.

The method addBefore puts a new node in front of the first node that holds a given value. The list has no empty node in front of its first value (a sentinel), so head is the first real node, or null when the list is empty.

Your readiness not started

Your own marking on this problem.

The contract

public boolean addBefore(Object target, Object data)

What the class already has:

The node the method looks for is the first node where target.equals(node.data) is true (the match). The checks at the top of a method that throw before anything changes are its guard. Here the guard throws IllegalArgumentException with the message addBefore was passed null.

InputResultSize after
target or data is null, on any list, empty includedThrows IllegalArgumentException. The list is unchanged.unchanged
No node equals target, the empty list includedReturns false. The list is unchanged.unchanged
The first node is the matchReturns true. The new node becomes head.up by 1
A later node is the matchReturns true. The new node goes between the match and the node in front of it.up by 1

Inserting in front of a found node

  1. Guard first. If target or data is null, throw. Nothing has changed yet, so the list is left as it was.
  2. Put match on this.head and set before to null. before is always the node in front of match, and it is null while match is on the first node.
  3. Loop while match is not null and target does not equal match.data. Each turn, before moves to match, then match moves to match.next.
  4. If match is null, no node matched. Return false and leave size alone.
  5. Build the new node with match as its next. It points at the match, and nothing points at it yet.
  6. Link it in. When before is null, the match is the first node, so head changes to the new node. Otherwise before.next changes to the new node.
  7. Add 1 to size and return true.

Step 6 has 2 branches because nothing sits in front of the first node. With a sentinel there would always be a node in front, and 1 branch would do.

public boolean addBefore(Object target, Object data) {
    if (target == null || data == null) {
        throw new IllegalArgumentException("addBefore was passed null");
    }
    Node before = null;
    Node match = this.head;
    while (match != null && !target.equals(match.data)) {
        before = match;
        match = match.next;
    }
    if (match == null) {
        return false;
    }
    Node added = new Node(data, match);
    if (before == null) {
        this.head = added;
    } else {
        before.next = added;
    }
    this.size++;
    return true;
}

The trace below runs addBefore("green", "pink") once on a list that holds red blue green, front to back.

StepbeforematchWhat changedsize
startnullrednothing3
turn 1redbluered is not green, so both step3
turn 2bluegreenblue is not green, so both step3
loop endsbluegreengreen equals the target3
buildbluegreena node holding pink now points at green. The list is unchanged.3
linkbluegreenblue.next is the new node. The list reads red blue pink green.3
endbluegreensize goes up, and the method returns true4

The cases

Each list is written front to back. Each case makes 1 call and prints the list after it.

CaseList beforeCallReturnsPrinted after
EmptyemptyaddBefore("blue", "pink")falsesize 0
Empty, null dataemptyaddBefore("blue", null)throwssize 0
1 node, matchblueaddBefore("blue", "pink")truesize 2: pink blue
1 node, no matchblueaddBefore("gray", "pink")falsesize 1: blue
Match at the frontred blue greenaddBefore("red", "pink")truesize 4: pink red blue green
Match in the middlered blue greenaddBefore("blue", "pink")truesize 4: red pink blue green
Match at the last nodered blue greenaddBefore("green", "pink")truesize 4: red blue pink green
No matchred blue greenaddBefore("gray", "pink")falsesize 3: red blue green
Target appears twicered blue green blueaddBefore("blue", "pink")truesize 5: red pink blue green blue
Null targetred blue greenaddBefore(null, "pink")throwssize 3: red blue green

The printed output of 3 of the cases, line for line. The label above each list comes from the test driver, not from toString.

Match at the front, returns true:
List size is: 4
pink
red
blue
green

Match at the last node, returns true:
List size is: 4
red
blue
pink
green

Null target, throws IllegalArgumentException: addBefore was passed null
List size is: 3
red
blue
green

Drill

Level-up check

1. Predict the output. A list holds red blue red green, front to back. The code calls addBefore("red", "pink") and prints the list. What comes out?

2. Spot the bug. This ending has no front case. The walk above it is correct. The list holds red blue and the call is addBefore("red", "pink"). What happens?

if (match == null) {
    return false;
}
before.next = new Node(data, match);
this.size++;
return true;

3. Pointer order. The list holds red blue, and the match is the first node. Each line below is followed by this.size++. Which line gives a list that prints size 3, then pink red blue?

4. Guard order. The list is empty and the call is addBefore("red", null). What happens?

4 questions. 3 right is a pass.

Write it

Write itThe guard

Write the guard for addBefore. It throws IllegalArgumentException with the message addBefore was passed null when target or data is null.

Write itThe walk to the match

Write the lines that leave match on the first node whose data equals target, with before on the node in front of it, and that return false when no node matches.

Your checklist

Tick these when they are true.

Could you write this cold?

Blank file, no notes, 4 hours on a clock, and only the Java API documentation open. Say yes only when that is true. You can take it back at any time.

Rules on the practice APE

You change only the lines between //startMethod and //endMethod, and you keep each method header as given.

A private helper method or field of your own between the markers is fine.

No new imports.

The official site's Basic Strategies page says: You may receive points for compiling code but you WON’T receive points for code that does not compile.

The addAtIndex problem in Practice exam 2 uses these moves.

Further reading