Notes for June 27, 2005

Java API Documentation

Programmers don't like re-inventing the wheel. Unfortunately, in this course, you will have to re-invent the wheel often, so you can truly understand algorithmic thinking and how to follow specifications. Try to be familiar with the API (Application Programming Interface) documentation. The documentation is split into 3 frames. The upper-left frame lists packages, the lower-left are the classes from the package, and the right frame is for the class itself. You will not need to know how to group things into packages--just know that things are organized into packages in Java. Of interest will be the java.lang and java.util packages. Knowing this will make it easier to browse the lower-left frame. Initially, the package setting is set to "All Classes" and finding a class in the lower-left frame can take a while. If you wanted information on the String class, click on java.lang in the upper-left frame, which updates the list on the lower-left frame. Clicking on String in the lower-left frame will update the right frame with the String class and all the relevant information about the class.

Types of Variables

In the second assignment, you have to turn characters into lowercase. Suppose we had a letter of type char. It would be nice to say:
    letter = letter.toLowerCase();
However, this is not feasible. Why not? letter is of type char, which is a primitive. It is not an object and thus cannot have methods to act on. Refer to the reading on the distinction between primitives and references.

Method Types

Getting a character in lowercase form is nothing new, so it is not surprising to know that the API has a method that does just that. The method is called toLowerCase in the Character class. It's header declares it as a static method:
public static char toLowerCase(char ch)
There are two types of methods: static (class) methods or instance methods. As the names should make clear, one type operates on the class level, while another operates at the instance level. A class method cannot access instance variables--it can only access class variables (those declared with the static keyword), whereas an instance method (those NOT declared with the static keyword) can access both. Why? Class variables are shared among all instances in the class.

To use a class method, the method call must be preceded by the class name similar to how calls to instance methods must be preceded by the particular instance. Thus, to turn a character to lowercase, the proper code is:

    letter = Character.toLowerCase(letter);

ASCII

Pretty much everyone knows that the computer only deals with ones and zeros. The computer has no notion of characters, so there needs to be a mapping from the bits to characters. Bits can be connected together to make numbers and so the mapping can be see as being between numbers and characters.

Long ago, some organization made the American Standard Code for Information Interchange (ASCII) that defines this mapping. Java actually uses Unicode, which is a superset of ASCII. In this course, we will only be interested in ASCII. The first thing to note from the table is that the letters are all sequential--at least all the uppercase letters and all the lowercase letters. In the assignment, you are asked to put counts of letters in an array of length 26. Since the letters are all in order, it is really easy to find which bin belongs to which letter. If you had a lowercase letter, you can find the bin it belongs to with the following code:

    letter - 'a'
How does this work? If letter was an a, then
    'a' - 'a' == 0
because 97 - 97 = 0. Thus, letter belongs in the bin with index 0. Look at the ASCII table to see if you can see where the 97 came from. Similarly, if letter was a b, then
    'b' - 'a' == 1
because 98 - 97 = 1.

If we have the bin number, how can we convert back to a char? Is it sufficient to add 'a', i.e., ('a' + x), where x is the bin number?

Not quite, because Java will convert the 'a' to 97 and then add that to x, resulting in an integer. To get the char, you have to cast that number into a character via:

    (char)('a' + x)
If x were 4, then the resulting character would be 'e', which is the fifth character of the alphabet. We will talk more about casting later when we talk about inheritance.

Character.isLetter

Knowing that characters of the same type are in order according to the ASCII table, how do we check if a character is a letter? We just have to check if the character is in the proper range! Assuming that letter is in lowercase:
    if ((letter >= 97) && (letter <= 122)) {
        // do something
    }
That is bad style, because it requires you to remember that 97 is the ASCII equivalent of 'a' and that 122 is the equivalent of 'z'. A much better way would be to let Java do the work for you:
    if ((letter >= 'a') && (letter <= 'z')) {
        // do something
    }
In fact, we do NOT want to see the first version! You have been warned!

Another way would be to take advantage of the API:

    if (Character.isLetter(letter)) {
        // do something
    }
then you don't even have to convert the character to lowercase first.

Linked Lists

The ListNode class is as follows:
    public class ListNode {
        public int data;
	public ListNode next;
    }
There are two things of interest here. First, the data fields are public. This is actually not the way one would implement nodes in the "real" world. The "right" way to do this would be to have ListNode be an inner class to the LinkedIntList class (or whatever class is using it), thus the outside world cannot see it. However, that's beyond the scope of this course, so we'll be satisfied with this way. Second, ListNode has a variable, next, that refers to the class itself. How is this possible? If you recall, all variables with object types are actually pointers to objects of that type. It is merely an address to a ListNode object. The compiler only checks that everything that is assigned to next is a ListNode.

Examples (courtesy of Stuart Reges)

Consider the problem of how to build up a set of nodes that would store the list (3, 7, 12). We'd have to have a variable of type ListNode:
    ListNode list;
The variable list is not itself a node. It's a variable that is capable of referring to a node. We don't have an actual node until we call new:
    ListNode list = new ListNode();
This constructs a new node and tells Java to have the variable list refer to it:
                    +------+------+
         +---+      | data | next |
    list | +-+--->  |      |      |
         +---+      +------+------+
What do we want to do with this node? We want to store 3 in its data field (list.data) and we want its next field to point to a new node:
    list.data = 3;
    list.next = new ListNode();
which leads us to this situation:
                    +------+------+      +------+------+
         +---+      | data | next |      | data | next |
    list | +-+--->  |   3  |   +--+--->  |      |      |
         +---+      +------+------+      +------+------+
Notice that the result of the call on new is not stored in list; it is stored in list.next. Now we want to assign the second node's data field (list.next.data) to the value 7 and assign the second node's next field to refer to a third node:
    list.next.data = 7;
    list.next.next = new ListNode();
which leads us to this situation:
                    +------+------+      +------+------+      +------+------+
         +---+      | data | next |      | data | next |      | data | next |
    list | +-+--->  |   3  |   +--+--->  |   7  |   +--+--->  |      |      |
         +---+      +------+------+      +------+------+      +------+------+
Finally, we want to set the data field of this third node to 12 (list.next.next.data) and we want to set its next field to null. The keyword null is a Java word that means "no object" (what i referred to in lecture as "nothingness"). This provides a "terminator" for the linked list (a special value that indicates that we are at the end of the list). So we'd execute these statements:
    list.next.next.data = 12;
    list.next.next.next = null;
which leaves us in this situation:
                    +------+------+      +------+------+      +------+------+
         +---+      | data | next |      | data | next |      | data | next |
    list | +-+--->  |   3  |   +--+--->  |   7  |   +--+--->  |  12  |   /  |
         +---+      +------+------+      +------+------+      +------+------+
We draw a diagonal line through the last next field as a way to indicate that it's value is null. Once we've set up this list, we can print its value as follows:
    System.out.println(list.data + " " + list.next.data + " " + list.next.next.data);
Obviously this is a very tedious way to manipulate a list. It's much better to write code that involves loops to manipulate lists. But it takes a while to get used to this idea, so we're first going to practice how to do some raw list operations without a loop. In section, we will go over 10 different exercises that involve list operations. Each will have a "before" picture and an "after" picture. The challenge is to write code that gets you from the one state to the other state. As an example, suppose that you have two variables of type ListNode called p and q and that you have the following situation:
                    +------+------+      +------+------+
         +---+      | data | next |      | data | next |
       p | +-+--->  |   2  |   +--+--->  |   4  |   /  |
         +---+      +------+------+      +------+------+

                    +------+------+      +------+------+
         +---+      | data | next |      | data | next |
       q | +-+--->  |   3  |   +--+--->  |   9  |   /  |
         +---+      +------+------+      +------+------+
and you want to get to this situation:
                    +------+------+      +------+------+      +------+------+
         +---+      | data | next |      | data | next |      | data | next |
       p | +-+--->  |   2  |   +--+--->  |   4  |   +--+--->  |   3  |   /  |
         +---+      +------+------+      +------+------+      +------+------+

                    +------+------+
         +---+      | data | next |
       q | +-+--->  |   9  |   /  |
         +---+      +------+------+
How do we do it? First, think about how many variables of type ListNode there are. Some would say two (p and q). Others would say four (p, q and the two non-null links). But in fact, there are six different variables of type ListNode.
           1                   2                    3
                    +------+------+      +------+------+
         +---+      | data | next |      | data | next |
       p | +-+--->  |   2  |   +--+--->  |   4  |   /  |
         +---+      +------+------+      +------+------+

           4                   5                    6
                    +------+------+      +------+------+
         +---+      | data | next |      | data | next |
       q | +-+--->  |   3  |   +--+--->  |   9  |   /  |
         +---+      +------+------+      +------+------+
Which of these variables has to change in value? The answer is that the boxes numbered 3, 4 and 5 have to be changed. If we change them appropriately, we'll be done. But care must be taken: order can be important. For example, we eventually want box 5 to be set to null, but we don't want to start with that:
    q.next = null;
If we do that, we'll lose track of our reference to the node with 9 in it. Of the three values, the one that is safe to change is box 3 because it's currently null. So we begin by setting it to point to the node with 3 in it:
    p.next.next = q;
Now that we've used the value of box 4 to reset box 3, we can reset box 4. It's supposed to point to the node that has 9 in it. We can do this by "leap frogging" over the current node it's pointing to:
    q = q.next;
Now we just have to reset box 5. But we can no longer refer to box 5 as q.next because we've changed q. Now, we have to refer to it this way:
    p.next.next.next = null;
Putting these three lines together, we see the code that is needed to get from the initial state to the final state:
    p.next.next = q;
    q = q.next;
    p.next.next.next = null;
Obviously this can be very confusing. It is essential that you draw pictures to keep track of what is pointing where and what is going on when this code executes. It's the only way to master linked list code.