1 of 98

Announcement

My office hours are in 779 Soda:

  • Mondays, 3:10 - 3:58 PM [ending a couple minutes early to get to lecture]
  • Thursdays, 2:00 - 3:00 PM

Come talk about anything.

  • Top priority: Conceptual / theoretical material related to 61B.
  • Bottom priority: Specific homework or project questions. Debugging.
  • Middle priority: Anything else (life questions, grad school, chatting about recent news, showing off your cool tattoo of a giant squid, whatever).

2 of 98

SLLists, Nested Classes, Sentinel Nodes

2

Lecture 4 (Lists 2)

CS61B, Fall 2025 @ UC Berkeley

Slides credit: Josh Hug

3 of 98

Getting Started:

IntList → IntNode

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

4 of 98

Last Time in 61B: Recursive Implementation of a List

While functional, “naked” linked lists like the one above are hard to use.

  • Users of this class are probably going to need to know references very well, and be able to think recursively. Let’s make our users’ lives easier.

public class IntList {

public int first;

public IntList rest;

public IntList(int f, IntList r) {

first = f;

rest = r;

}

...

}

5 of 98

Improvement #1: Rebranding and Culling

public class IntNode {

public int item;

public IntNode next;

public IntNode(int i, IntNode n) {

item = i;

next = n;

}

}

Not much of an improvement obviously, but this next weird trick will be more impressive.

IntNode is now dumb, has no methods. We will reintroduce functionality in the coming slides.

6 of 98

Creating the SLList Class

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

7 of 98

Coding Demo: Bureaucracy

/** An SLList is a list of integers, which hides the terrible truth

* of the nakedness within. */

public class SLList {

}

SLList.java

8 of 98

Coding Demo: Bureaucracy

/** An SLList is a list of integers, which hides the terrible truth

* of the nakedness within. */

public class SLList {

public IntNode first;

}

SLList.java

9 of 98

Coding Demo: Bureaucracy

/** An SLList is a list of integers, which hides the terrible truth

* of the nakedness within. */

public class SLList {

public IntNode first;

public SLList(int x) {

}

}

SLList.java

10 of 98

Coding Demo: Bureaucracy

/** An SLList is a list of integers, which hides the terrible truth

* of the nakedness within. */

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

}

SLList.java

11 of 98

Coding Demo: Bureaucracy

/** An SLList is a list of integers, which hides the terrible truth

* of the nakedness within. */

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public static void main(String[] args) {

}

}

SLList.java

12 of 98

Coding Demo: Bureaucracy

/** An SLList is a list of integers, which hides the terrible truth

* of the nakedness within. */

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public static void main(String[] args) {

/** Creates a list of one integer, namely 10 */

SLList L = new SLList(10);

}

}

SLList.java

13 of 98

Improvement #2: Bureaucracy

public class IntNode {

public int item;

public IntNode next;

public IntNode(int i, IntNode n) {

item = i;

next = n;

}

}

SLList is easier to instantiate (no need to specify null), but we will see more advantages to come.

Next: Let’s add addFirst and getFirst methods to SLList.

IntNode is now dumb, has no methods.

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

...

}

IntList X = new IntList(10, null);

SLList Y = new SLList(10);

14 of 98

addFirst and getFirst

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

15 of 98

Coding Demo: addFirst and getFirst

public class SLList {

public IntNode first;

}

SLList.java

16 of 98

Coding Demo: addFirst and getFirst

public class SLList {

public IntNode first;

/** Adds x to the front of the list. */

public void addFirst(int x) {

}

}

SLList.java

17 of 98

Coding Demo: addFirst and getFirst

public class SLList {

public IntNode first;

/** Adds x to the front of the list. */

public void addFirst(int x) {

first = new IntNode(x, first);

}

}

SLList.java

This is how we added to the front of an IntList in the previous lecture.

18 of 98

Coding Demo: addFirst and getFirst

public class SLList {

public IntNode first;

/** Adds x to the front of the list. */

public void addFirst(int x) {

first = new IntNode(x, first);

}

/** Returns the first item in the list. */

public int getFirst() {

}

}

SLList.java

19 of 98

Coding Demo: addFirst and getFirst

public class SLList {

public IntNode first;

/** Adds x to the front of the list. */

public void addFirst(int x) {

first = new IntNode(x, first);

}

/** Returns the first item in the list. */

public int getFirst() {

return first.item;

}

}

SLList.java

20 of 98

Coding Demo: addFirst and getFirst

public class SLList {

public IntNode first;

public static void main(String[] args) {

SLList L = new SLList(15);

L.addFirst(10);

L.addFirst(5);

System.out.println(L.getFirst()); // should print 5

}

}

SLList.java

21 of 98

The Basic SLList and Helper IntNode Class

public class IntNode {

public int item;

public IntNode next;

public IntNode(int i, IntNode n) {

item = i;

next = n;

}

}

Example

usage:

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public void addFirst(int x) {

first = new IntNode(x, first);

}

public int getFirst() {

return first.item;

}

}

SLList L = new SLList(15);

L.addFirst(10);

L.addFirst(5);

int x = L.getFirst();

22 of 98

SLLists vs. IntLists

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

23 of 98

SLLists vs. IntLists

While functional, “naked” linked lists like the IntList class are hard to use.

  • Users of IntList are need to know Java references well, and be able to think recursively.
  • SLList is much simpler to use. Simply use the provided methods.
  • Why not just add an addFirst method to the IntList class? Turns out there is no efficient way to do this. Try it out and you’ll see it’s hard (and inefficient).

IntList L = new IntList(15, null);

L = new IntList(10, L);

L = new IntList(5, L);

int x = L.first;

SLList L = new SLList(15);

L.addFirst(10);

L.addFirst(5);

int x = L.getFirst();

24 of 98

Naked Linked Lists (IntList) vs. SLLists

L1

addFirst()

SLList class acts as a middle man between user and raw data structure.

first

getFirst()

5

10

15

L2

L1

item

next

5

10

15

first

rest

Naked recursion: Natural for IntList user to have variables that point to the middle of the IntList.

25 of 98

Access Control: Public vs. Private

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

26 of 98

The SLList So Far

SLList L = new SLList(15);

L.addFirst(10);

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public void addFirst(int x) {

first = new IntNode(x, first);

}

...

}

L

addFirst()

first

getFirst()

item

next

10

15

27 of 98

A Potential SLList Danger

L

addFirst()

first

getFirst()

item

next

10

15

Users of our class might be tempted to try to manipulate our secret IntNode directly in uncouth ways!

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public void addFirst(int x) {

first = new IntNode(x, first);

}

...

}

SLList L = new SLList(15);

L.addFirst(10);

L.first.next.next = L.first.next;

28 of 98

A Potential SLList Danger

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public void addFirst(int x) {

first = new IntNode(x, first);

}

...

}

SLList L = new SLList(15);

L.addFirst(10);

L.first.next.next = L.first.next;

Users of our class might be tempted to try to manipulate our secret IntNode directly in uncouth ways!

L

addFirst()

first

getFirst()

item

next

10

15

29 of 98

Access Control

public class SLList {

public IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public void addFirst(int x) {

first = new IntNode(x, first);

}

...

}

We can prevent programmers from making such mistakes with the private keyword.

30 of 98

Improvement #3: Access Control

SLList L = new SLList(15);

L.addFirst(10);

L.first.next.next = L.first.next;

public class SLList {

private IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public void addFirst(int x) {

first = new IntNode(x, first);

}

...

}

Use the private keyword to prevent code in other classes from using members (or constructors) of a class.

jug ~/Dropbox/61b/lec/lists2

$ javac SLListUser.java

SLListUser.java:8: error: first has private access in SLList

L.first.next.next = L.first.next;

31 of 98

Why Restrict Access?

Hide implementation details from users of your class.

  • Less for user of class to understand.
  • Safe for you to change private methods (implementation).

Car analogy:

  • Public: Pedals, Steering Wheel Private: Fuel line, Rotary valve

  • Despite the term ‘access control’:
    • Nothing to do with protection against hackers, spies, and other evil entities.

32 of 98

Nested Classes

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

33 of 98

Improvement #4: Nested Classes

Can combine two classes into one file pretty simply.

public class SLList {

public class IntNode {

public int item;

public IntNode next;

public IntNode(int i, IntNode n) {

item = i;

next = n;

}

}

private IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

} ...

Nested class definition.

Could have made IntNode a private nested class if we wanted.

Instance variables, constructors, and methods of SLList typically go below nested class definition.

34 of 98

Why Nested Classes?

Nested Classes are useful when a class doesn’t stand on its own and is obviously subordinate to another class.

  • Make the nested class private if other classes should never use the nested class.

In my opinion, probably makes sense to make IntNode a nested private class.

  • Hard to imagine other classes having a need to manipulate IntNodes.
  • If there was some hypothetical strange function like:�public IntNode getFrontNode()�Then we would need the IntNode class to be public.

35 of 98

Static Nested Classes

If the nested class never uses any instance variables or methods of the outer class, declare it static.

  • Static classes cannot access outer class’s instance variables or methods.
  • Results in a minor savings of memory. See book for more details / exercise.

public class SLList {

private static class IntNode {

public int item;

public IntNode next;

public IntNode(int i, IntNode n) {

item = i;

next = n;

}

...

}

We can declare IntNode static, since it never uses any of SLList’s instance variables or methods.

Analogy: Static methods had no way to access “my” instance variables. Static classes cannot access “my” outer class’s instance variables.

Unimportant note: For private nested classes, access modifiers are irrelevant.

36 of 98

Creating addLast and size

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

37 of 98

Adding More SLList Functionality

To motivate our remaining improvements, and to give more functionality to our SLList class, let’s add:

  • .addLast(int x)
  • .size()

Recommendation: Try writing them yourself before watching how I do it.

Methods

Non-Obvious Improvements

addFirst(int x)

#1

Rebranding: IntListIntNode

getFirst

#2

Bureaucracy: SLList

#3

Access Control: publicprivate

#4

Nested Class: Bringing IntNode into SLList

See lectureCode Repository for starter code!

  • lec4_lists2/addLastAndSizeExercise

38 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Adds an item to the end of the list. */

public void addLast(int x) {

}

}

SLList.java

39 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Adds an item to the end of the list. */

public void addLast(int x) {

IntNode p = first;

}

}

SLList.java

40 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Adds an item to the end of the list. */

public void addLast(int x) {

IntNode p = first;

while (p.next != null) {

}

}

}

SLList.java

41 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Adds an item to the end of the list. */

public void addLast(int x) {

IntNode p = first;

while (p.next != null) {

p = p.next;

}

}

}

SLList.java

42 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Adds an item to the end of the list. */

public void addLast(int x) {

IntNode p = first;

/* Move p until it reaches the end of the list. */

while (p.next != null) {

p = p.next;

}

}

}

SLList.java

43 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Adds an item to the end of the list. */

public void addLast(int x) {

IntNode p = first;

/* Move p until it reaches the end of the list. */

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

}

SLList.java

44 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

public static void main(String[] args) {

SLList L = new SLList(15);

L.addFirst(10);

L.addFirst(5);

L.addLast(20);

System.out.println(L.getFirst()); // should print 5

}

}

SLList.java

Running this code doesn't actually prove that our addLast works. More about testing in a later lecture.

45 of 98

Coding Demo: size

public class SLList {

private IntNode first;

public int size() {

}

}

SLList.java

46 of 98

Coding Demo: size

public class SLList {

private IntNode first;

public int size() {

}

}

SLList.java

Writing a recursive size method is tricky, because SLList itself is not recursive.

The size method doesn't take in any arguments, so calling size recursively is strange. What is the base case? How do you call size recursively to get closer to the base case?

47 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

private static int size(IntNode p) {

}

public int size() {

}

}

SLList.java

Solution: Write a private static helper method that takes in an extra argument to help with recursion.

(This can be static because we don't need to reference first.)

48 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Returns the size of the list that starts at IntNode p. */

private static int size(IntNode p) {

}

public int size() {

}

}

SLList.java

49 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Returns the size of the list that starts at IntNode p. */

private static int size(IntNode p) {

if (p.next == null) {

}

}

public int size() {

}

}

SLList.java

50 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Returns the size of the list that starts at IntNode p. */

private static int size(IntNode p) {

if (p.next == null) {

return 1;

}

}

public int size() {

}

}

SLList.java

51 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Returns the size of the list that starts at IntNode p. */

private static int size(IntNode p) {

if (p.next == null) {

return 1;

}

return 1 + size(p.next);

}

public int size() {

}

}

SLList.java

52 of 98

Coding Demo: addLast

public class SLList {

private IntNode first;

/** Returns the size of the list that starts at IntNode p. */

private static int size(IntNode p) {

if (p.next == null) {

return 1;

}

return 1 + size(p.next);

}

public int size() {

return size(first);

}

}

SLList.java

53 of 98

Private Recursive Helper Methods

To implement a recursive method in a class that is not itself recursive (e.g. SLList):

  • Create a private recursive helper method.
  • Have the public method call the private recursive helper method.

public class SLList {

private IntNode first;

private int size(IntNode p) {

if (p.next == null) {

return 1;

}

return 1 + size(p.next);

}

public int size() {

return size(first);

}

}

54 of 98

size Efficiency, caching

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

55 of 98

Efficiency of Size: https://www.yellkey.com/second

How efficient is size?

  • Suppose size takes 2 seconds on a list of size 1,000.
  • How long will it take on a list of size 1,000,000?

  1. 0.002 seconds.
  2. 2 seconds.
  3. 2,000 seconds.
  4. 2,000,000 seconds.

public class SLList {

private IntNode first;

private int size(IntNode p) {

if (p.next == null) {

return 1;

}

return 1 + size(p.next);

}

public int size() {

return size(first);

}

}

56 of 98

Improvement #5: Fast size()

Your goal:

  • Modify SLList so that the execution time of size() is always fast (i.e. independent of the size of the list).

(video viewers only, time is too tight in class to think carefully about this)

public class SLList {

private IntNode first;

public SLList(int x) {

first = new IntNode(x, null);

}

public void addFirst(int x) {

first = new IntNode(x, first);

}

private int size(IntNode p) {

if (p.next == null)

return 1;

return 1 + size(p.next);

}

public int size() {

return size(first);

}

}

57 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

}

SLList.java

Instead of re-calculating size on demand every time size is called, we'll keep track of the current size in a private variable.

Then, we'll update the size variable every time the list is changed.

This variable is redundant (we could calculate the size from the list), but will save us time.

58 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

}

}

SLList.java

59 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

size = 1;

}

}

SLList.java

New line added to existing function.

60 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

/** Adds x to the front of the list. */

public void addFirst(int x) {

first = new IntNode(x, first);

}

}

SLList.java

61 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

/** Adds x to the front of the list. */

public void addFirst(int x) {

first = new IntNode(x, first);

size += 1;

}

}

SLList.java

New line added to existing function.

62 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

/** Returns the first item in the list. */

public int getFirst() {

return first.item;

}

}

SLList.java

No modification needed. getFirst doesn't change the size of the list.

63 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

/** Adds x to the end of the list. */

public void addLast(int x) {

IntNode p = first;

/** Move p until it reaches the end of the list. */

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

}

SLList.java

64 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

/** Adds x to the end of the list. */

public void addLast(int x) {

size += 1;

IntNode p = first;

/** Move p until it reaches the end of the list. */

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

}

SLList.java

New line added to existing function.

65 of 98

Coding Demo: Fast size

public class SLList {

private IntNode first;

private int size;

public int size() {

return size;

}

}

SLList.java

66 of 98

Improvement #5: Fast size()

Solution: Maintain a special size variable that caches the size of the list.

  • Caching: putting aside data to speed up retrieval.

TANSTAAFL: There ain't no such thing as a free lunch.

  • But spreading the work over each add call is a net win in almost any circumstance.

67 of 98

Naked Linked Lists (IntList) vs. SLLists

x

addFirst()

SLList class acts as a middle man between user and the naked recursive data structure. Allows us to store meta information about entire list, e.g. size.

first

getFirst()

5

10

15

x

item

next

5

10

15

first

rest

3

size

size()

addLast()

68 of 98

The Empty List and the addLast Bug

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

69 of 98

Coding Demo: The Empty List

public class SLList {

private IntNode first;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

size = 1;

}

/** Creates an empty SLList. */

public SLList() {

}

}

SLList.java

Adding a second constructor for empty lists (in addition to the existing constructor for making lists with 1 element).

70 of 98

Coding Demo: The Empty List

public class SLList {

private IntNode first;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

size = 1;

}

/** Creates an empty SLList. */

public SLList() {

size = 0;

}

}

SLList.java

Adding a second constructor for empty lists (in addition to the existing constructor for making lists with 1 element).

71 of 98

Coding Demo: The Empty List

public class SLList {

private IntNode first;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

size = 1;

}

/** Creates an empty SLList. */

public SLList() {

first = null;

size = 0;

}

}

SLList.java

Adding a second constructor for empty lists (in addition to the existing constructor for making lists with 1 element).

72 of 98

Coding Demo: The Empty List

public class SLList {

private IntNode first;

private int size;

public static void main(String[] args) {

SLList L = new SLList();

L.addFirst(10);

L.addFirst(5);

L.addLast(20);

System.out.println(L.getFirst()); // should print 5

}

}

SLList.java

73 of 98

Improvement #6a: Representing the Empty List

Benefits of SLList vs. IntList so far:

  • Faster size() method than would have been convenient for IntList.
  • User of an SLList never sees the IntList class.
    • Simpler to use.
    • More efficient addFirst method (see exercises).
    • Avoids errors (or malfeasance):

Another benefit we can gain:

  • Easy to represent the empty list. Represent the empty list by setting first to null. Let’s try!
  • We’ll see there is a very subtle bug in the code. It crashes when you call addLast on an empty list.

15

10

74 of 98

Coding Demo: The addLast Bug

public class SLList {

private IntNode first;

private int size;

public static void main(String[] args) {

SLList L = new SLList();

L.addLast(20); // program crashes!

}

}

SLList.java

75 of 98

Coding Demo: The addLast Bug

public class SLList {

private IntNode first;

private int size;

/** Adds x to the end of the list. */

public void addLast(int x) {

size += 1;

IntNode p = first;

/** Move p until it reaches the end of the list. */

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

}

SLList.java

The NullPointerException traceback identifies this line as where the program crashed.

76 of 98

How Would You Fix addLast?

Your goal:

  • Fix addLast so that we do not get a null pointer exception when we try to add to the back of an empty SLList:

See lec4_lists2/fixAddLastBug in the lectureCode-sp25 repo if you want to try on a computer.

public class SLList {

private IntNode first;

private int size;

public SLList() {

first = null;

size = 0;

}

public void addLast(int x) {

size += 1;

IntNode p = first;

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

}

SLList s1 = new SLList();

s1.addLast(5);

77 of 98

One Solution

One possible solution:

  • Add a special case for the empty list.

But there are other ways...

public void addLast(int x) {

size += 1;

if (first == null) {

first = new IntNode(x, null);

return;

}

IntNode p = first;

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

78 of 98

Sentinel Nodes

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

79 of 98

Tip For Being a Good Programmer: Keep Code Simple

As a human programmer, you only have so much working memory.

  • You want to restrict the amount of complexity in your life!
  • Simple code is (usually) good code.
    • Special cases are not ‘simple’.

public void addLast(int x) {

size += 1;

if (first == null) {

first = new IntNode(x, null);

return;

}

IntNode p = first;

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

80 of 98

addLast’s Fundamental Problem

The fundamental problem:

  • The empty list has a null first. Can’t access first.next!

public void addLast(int x) {

size += 1;

if (first == null) {

first = new IntNode(x, null);

return;

}

IntNode p = first;

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

Our fix is a bit ugly:

  • Requires a special case.
  • More complex data structures will have many more special cases (gross!!)

How can we avoid special cases?

  • Make all SLLists (even empty) the “same”.

81 of 98

Improvement #6b: Representing the Empty List Using a Sentinel

Create a special node that is always there! Let’s call it a “sentinel node”.

0

addFirst()

first

getFirst()

item

next

??

size

size()

addLast()

addFirst()

first

getFirst()

item

next

??

5

15

10

3

size

size()

addLast()

The empty list is just the sentinel node.

A list with 3 numbers has a sentinel node and 3 nodes that contain real data.

Let’s try reimplementing SLList with a sentinel node.

82 of 98

Coding Demo: Sentinel Nodes

public class SLList {

private IntNode first;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

size = 1;

}

public SLList() {

first = null;

size = 0;

}

}

SLList.java

In this demo, we'll be modifying our existing code to account for the sentinel node.

83 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

size = 1;

}

public SLList() {

first = null;

size = 0;

}

}

SLList.java

Renaming first to sentinel.

84 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

public SLList(int x) {

first = new IntNode(x, null);

size = 1;

}

public SLList() {

sentinel = new IntNode(63, null);

size = 0;

}

}

SLList.java

The empty list is no longer null. It's the sentinel node (and no other nodes).

63 is a placeholder. We don't care about the sentinel node's value.

85 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

public SLList(int x) {

sentinel = new IntNode(63, null);

sentinel.next = new IntNode(x, null);

size = 1;

}

public SLList() {

sentinel = new IntNode(63, null);

size = 0;

}

}

SLList.java

The single-element list is no longer just one node. It's two nodes: the sentinel node, followed by the node with the single value.

86 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

/** Adds x to the front of the list. */

public void addFirst(int x) {

first = new IntNode(x, first);

size += 1;

}

}

SLList.java

Need to modify addFirst to be consistent with the sentinel structure.

87 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

/** Adds x to the front of the list. */

public void addFirst(int x) {

sentinel.next = new IntNode(x, sentinel.next);

size += 1;

}

}

SLList.java

We need to reassign sentinel.next here, because "the first item is at sentinel.next."

88 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

/** Returns the first item in the list. */

public int getFirst() {

return first.item;

}

}

SLList.java

Need to modify getFirst to be consistent with the sentinel structure.

89 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

/** Returns the first item in the list. */

public int getFirst() {

return sentinel.next.item;

}

}

SLList.java

Recall: "the first item is at sentinel.next."

If we returned sentinel.item, we would get the placeholder 63, not the true first item of the list.

90 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

/** Adds x to the end of the list. */

public void addLast(int x) {

size += 1;

IntNode p = first;

/** Move p until it reaches the end of the list. */

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

}

SLList.java

Need to modify addLast to be consistent with the sentinel structure.

91 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

/** Adds x to the end of the list. */

public void addLast(int x) {

size += 1;

IntNode p = sentinel;

/** Move p until it reaches the end of the list. */

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

}

SLList.java

Start p at the sentinel, and move p until it reaches the end of the list.

Sentinel always exists, so the addLast bug doesn't apply anymore.

92 of 98

Coding Demo: Sentinel Nodes

public class SLList {

/** The first item (if it exists) is at sentinel.next. */

private IntNode sentinel;

private int size;

public static void main(String[] args) {

SLList L = new SLList();

L.addLast(20);

System.out.println(L.size()); //should print out 1

}

}

SLList.java

93 of 98

Sentinel Node

The sentinel node is always there for you.

Notes:

  • I’ve renamed first to be sentinel.
  • sentinel is never null, always points to sentinel node.
  • Sentinel node’s item needs to be some integer, but doesn’t matter what value we pick.
  • Had to fix constructors and methods to be compatible with sentinel nodes.

0

addFirst()

sentinel

getFirst()

item

next

63

size

size()

addLast()

addFirst()

sentinel

getFirst()

item

next

63

5

15

10

3

size

size()

addLast()

94 of 98

addLast (with Sentinel Node)

Bottom line: Having a sentinel simplifies our addLast method.

  • No need for a special case to check if sentinel is null (since it is never null).

public void addLast(int x) {

size += 1;

if (sentinel == null) {

sentinel = new IntNode(x, null);

return;

}

IntNode p = sentinel;

while (p.next != null) {

p = p.next;

}

p.next = new IntNode(x, null);

}

0

addFirst()

sentinel

getFirst()

item

next

??

size

size()

addLast()

95 of 98

Invariants

Lecture 4, CS61B, Fall 2025

Getting Started

  • IntListIntNode
  • Creating the SLList Class
  • addFirst and getFirst
  • SLLists vs. IntLists

Syntax Improvements:

  • Access Control: Public vs. Private
  • Nested Classes

addLast and size

  • Creating addLast and size
  • size Efficiency, caching

The Empty List

  • addLast Bug
  • Sentinel Nodes
  • Invariants

96 of 98

Invariants

An invariant is a condition that is guaranteed to be true during code execution (assuming there are no bugs in your code).

An SLList with a sentinel node has at least the following invariants:

  • The sentinel reference always points to the sentinel node.
  • The first node (if it exists), is always at sentinel.next.
  • The size variable is always the total number of items that have been added.

Invariants make it easier to reason about code:

  • Can assume they are true to simplify code (e.g. addLast doesn’t need to worry about nulls).
  • Must ensure that methods preserve invariants.

97 of 98

Summary

Methods

Non-Obvious Improvements

addFirst(int x)

#1

Rebranding: IntListIntNode

getFirst

#2

Bureaucracy: SLList

size

#3

Access Control: publicprivate

addLast(int x)

#4

Nested Class: Bringing IntNode into SLList

#5

Caching: Saving size as an int.

#6

Generalizing: Adding a sentinel node to allow representation of the empty list.

98 of 98

Next up: Stacks Homework

If today felt like a lot to digest, don’t worry.

  • The Stack class that you’ll build in HW5 will give you practice.
  • If possible, try to complete before lecture next Monday.

Attendance link: https://www.yellkey.com/sister