Review¶
In class las time, we started the task of implementing Java’s LinkedList
from scratch. Our last class was all about the fundamental building block for the LinkedList
called a ListNode
. As a reminder, the ListNode
was defined as:
public class ListNode {
public int data;
public ListNode next;
}
Which we link together by manually by writing code like
public class ListClient {
public static void main(String[] args) {
ListNode front;
front = new ListNode();
front.data = 42;
front.next = new ListNode();
front.next.data = -3;
front.next.next = new ListNode();
front.next.next.data = 17;
front.next.next.next = new ListNode();
front.next.next.next.data = 9;
// Builds up: [42, -3, 17, 9]
}
}
Which produces a list like the one we saw in class
Constructors¶
The code is a bit more complicated than necessary because we don’t provide a constructor that just sets the node up with the data desired. To fix this, we can provide a constructor:
public ListNode(int data) {
this.data = data;
}
Which then lets us simplify the client code to
public class ListClient {
public static void main(String[] args) {
ListNode front;
front = new ListNode(42);
front.next = new ListNode(-3);
front.next.next = new ListNode(17);
front.next.next.next = new ListNode(9);
}
}
We can even simplify this further by providing a constructor that also sets .next
for us since we know what we want the .next
to be right away. Here is the finalized version of the class and the client method that uses a constructor that takes both an int
and ListNode
.
public class ListNode {
public int data;
public ListNode next;
public ListNode() {
this(0, null);
}
public ListNode(int data) {
this(data, null);
}
public ListNode(int data, ListNode next) {
this.data = data;
this.next = next;
}
}
public class ListClient {
public static void main(String[] args) {
ListNode front = new ListNode(42, new ListNode(-3, new ListNode(17, new ListNode(9))));
}
}
.data
and .next
fields since they are handled by the constructors. Making ListNode
Code More General¶
In both classes, students asked is there a better way of handling these nodes without having to write out .next.next.next...
. This will be the focus on today’s class, but we will introduce the concept for the first time here before starting class reviewing this idea.
Consider we had a variable list
with many nodes that we wanted to print out each value. We could start by trying to write something like:
System.out.print(list.data + " ");
System.out.print(list.next.data + " ");
System.out.print(list.next.next.data + " ");
System.out.print(list.next.next.next.data + " ");
...
This will never work because we might not know how long this list is beforehand so we won’t know how many print statements to write. Additionally, we might run into a NullPointerException
if the list was shorter and went too far down the list with too many .next
s.
A NullPointException
occurs when you try to access the field of null
reference. For example, if you ran the following code, you would get a NullPointerException
since we tried to access the data
field of null
. Since null
has no fields, it yields a NullPointerException
.
ListNode list = new ListNode(4);
System.out.println(list.next.data); // NullPointerException
Instead, we will need a new approach that somehow traverses the list by looking at each element in turn. This will be quite similar to how we traverse arrays, but with a few key differences since we don’t have indexes or a defined size for our LinkedIntList
.
Key Idea
Use a local variable that moves through the list. It will keep track of the current node, do some computation (like printing), before moving to the next node.
We will quickly just show the final version code now, and then we will note why it is written this way below. It is okay if this doesn’t make sense now, we just want to expose you to this code pattern before we walk through it in class today.
ListNode current = list;
while (current != null) {
System.out.print(current.data + " ");
current = current.next;
}
What is this doing?
current
is a variable that stores a reference to the current node we are looking at. It starts out at the front of the list, and on each iteration of the loop gets updated to point to the next node in the list.- We want to stop the loop once we have reached
null
. It’s okay forcurrent
to becomenull
, but we must never say.data
or.next
oncurrent
if it isnull
.- One way of thinking about this: inside the loop we say
current.data
andcurrent.next
so we have to make surecurrent
is notnull
(hence the loop condition).
- One way of thinking about this: inside the loop we say
Here is a gif showing how the variables change throughout the program: