Review: here the representation we've been using for links in Java.
public class Link { // a single node in a linked list public Object item; // data item referenced by this link public Link next; // next node in the list or null if no next node // construct a new Link with given item and next fields public Link(Object item, Link next) { this.item = item; this.next = next; } // construct a new Link without initializing any fields public Link() { this(null, null); } }
When we want to create a list, we'll create a suitable variable somewhere to refer to the first link (assuming the list is not empty), for example,
Link head; // reference to first node in list, or null if list is empty
Eventually, we're going to use links
to create versions of data structures like our StringList
, only with a linked
list underneath instead of an array. But before then we need to gain some facility
working with linked lists to perform various tasks.
Many algorithms for processing a linked list have the following form:
start at the beginning (the first link)
while we haven't reached the end of the list {
process the data referenced by the current link
advance to the next link
}
We can refine this and move closer to real code by filling in some details:
Link p = head;
while (p != null) {
process the data referred to by link p
;
p = p.next;
}
This is an example of a program schema or template. It gives us a standard way of processing a list without giving the particular details of what needs to be done with the data in each node. The exact processing for each node will depend on what we are trying to do, but the overall schema will be the same regardless of what we are doing.
This sort of pattern appears often in programming: think about tasks like "go through an array and do x", or "read through the input until the end of the file and do y". In many of these cases there is a standard schema or template for the overall organization of the code, but the details will differ. Learning to recognize situations like this and adding standard ways of doing things to your toolkit is a significant part of developing into a good, experienced programmer.
Let's work out a detailed example. Suppose head
points to a linked
list whose nodes contain the strings "apple", "banana", and "cherry". Draw
a picture of the list:
Now, let's write code to print out all of the words in the list. Since the job involves traversing a list and doing something at each node, we can start by writing down our standard schema for traversing a list:
Link p = head;
while (p != null) {
process the data referred to by link p
;
p = p.next;
}
What do we need to do to process each node? In this case, just print it! So
we can substitute a println
statement for the "process the
data" step
and we get
Link p = head;
while (p != null) {
System.out.println(p.item);
p = p.next;
}
Does this work? How do we know? Answer: a first way is to "desk-check" our work by simulating the operation of the computer by hand. Do that here. (You'll need to draw a picture of the list showing how p is updated, as well as leave some room to write down the output that's produced.)
Is the code right? Does it work on an ordinary list? Does it work on edge cases? (What if the list is empty?)
Now let's try a couple more problems. Recall that our schema for processing a list is
Link p = head;
while (p != null) {
process the data referred to by link p
;
p = p.next;
}
Problem: Print the number of nodes in the list. What do we need to do inside the loop? What do we need to do before or after the loop?
Your code here: (and also, hand-simulate it to be sure that it works - both in the ordinary case and for unusual cases like an empty list)
Another problem: print "yes" if the list contains the string s
(which we assume
has been initialized somewhere else), otherwise print "no".
One final thought on while loops vs for loops. The Java (and C/C++) for loop can be used to write any code that can be written as a while loop. Some people have strong preferences one way or the other, but it's basically a matter of taste.