CSE143 Notes for Monday, 4/5/10

I said that we were going to discuss various details associated with the ArrayList class and our ArrayInList class. We wrote some code to construct an ArrayIntList and to fill it up with values:

        public class ArrayListExample {
            public static void main(String[] args) {
        	ArrayIntList list = new ArrayIntList();
        	list.add(12);
        	list.add(3);
        	list.add(3);
        	list.add(72);
        	list.add(42);
        	list.add(3);
        	list.add(-19);
        	System.out.println("list = " + list);
            }
        }
When we ran it, it produced the following output:

        list = [12, 3, 3, 72, 42, 3, -19]
I then asked how we could find the sum of the values in the list. Someone said we should use a for loop and that we should use the size method of ArrayIntList to figure out how many times to loop and that we should use the get method of ArrayIntList to get the individual values:

	int sum = 0;
	for (int i = 0; i < list.size(); i++) {
	    sum += list.get(i);
	}
	System.out.println("sum = " + sum);
When we ran this new version of the program, it produced the following output:

        list = [12, 3, 3, 72, 42, 3, -19]
        sum = 116
This is often a reasonable way to manipulate a list, but I said that I wanted to explore a different approach using what is known as an iterator. The code we've written to sum the list works, but it relies on the "get" method being able to quickly access any element of the structure. This property is known as random access. We say that arrays and the ArrayList and ArrayIntList classes that are built on top of them are random access structures because we can quickly access any element of the structure. If you knew that for the rest of your life, you'd always be working with arrays, then you'd have little use for iterators. You'd just call the get method because with arrays you get fast random access.

But many of the other data structures we will be looking at don't have this kind of quick access. Think of how a DVD works, quickly jumping to any scene, or how a CD works, quickly jumping to any track, versus how a VHS tape works, requiring you to fast forward through every scene until you get to the one you want. For those other structures we will study, iterators will make a lot more sense. So iterators will seem a bit silly when tied to an array-based structure, but we'll eventually see much more interesting examples of iterators.

In general, we think of an iterator as having three basic operations:

Sun adopted the convention early on that the second and third steps would be combined into one operation known as "next" that does two different things: it returns the next value and it advances the iterator to the next value. So in Java there are two fundamental operations:

We wrote a loop to find the product of the values in the list by using an iterator:

	ArrayIntListIterator i = list.iterator();
	int product = 1;
	while (i.hasNext()) {
	    int next = i.next();
	    product *= next;
	}
	System.out.println("product = " + product);
This involves a new kind of object of type ArrayIntListIterator. Once we have our iterator, we can use it to go through the values in the list one at a time.

To make this even more clear, we added a println inside the loop:

	ArrayIntListIterator i = list.iterator();
	int product = 1;
	while (i.hasNext()) {
	    int next = i.next();
     	    System.out.println("product = " + product + ", next = " + next);
	    product *= next;
	}
	System.out.println("product = " + product);
This loop produced the following output:

        product = 1, next = 12
        product = 12, next = 3
        product = 36, next = 3
        product = 108, next = 72
        product = 7776, next = 42
        product = 326592, next = 3
        product = 979776, next = -19
        product = -18615744
        list = [12, 72, 42, -19]
I also briefly mentioned that iterators often support a method called remove that allows you to remove the value that you most recently get from a call on next. For example, this variation of the code prints each value and removes any occurrences of the value 3:

	ArrayIntListIterator i = list.iterator();
	int product = 1;
	while (i.hasNext()) {
	    int next = i.next();
     	    System.out.println("product = " + product + ", next = " + next);
	    product *= next;
            if (next == 3)
                i.remove();
	}
	System.out.println("product = " + product);
This code examines each value in the list and removes all the occurrences of 3.

I then spent some time talking about the built-in ArrayList class. Remember that we're studying the ArrayIntList class as a way to understand the built-in ArrayList class. I first had to discuss relatively new feature of Java known as "generics." We know that for arrays, it is possible to construct arrays that store different types of data:

But arrays are a special case in Java. If they didn't already exist, we couldn't easily add them to Java. Instead, Java now allows you to declare generic classes and generic interfaces. For example, the ArrayList class is similar to an array. Instead of declaring ordinary ArrayList objects, we declare ArrayList<E> where E is some type (think of E as being short for "Element type"). The "E" is a type parameter that can be filled in with the name of any class.

For example, suppose, we want an ArrayList of Strings. We describe the type as:

        ArrayList<String>
When we construct an ArrayIntList, we say:
        ArrayIntList lst = new ArrayIntList();
Imagine replacing both occurrences of "ArrayIntList" with "ArrayList<String>" and you'll see how to construct an ArrayList<String>:
        ArrayList<String> lst = new ArrayList<String>();
And in the same way that you would declare a method header for manipulating an ArrayIntList object:

        public void doSomethingCool(ArrayIntList lst) {
            ...
        }
You can use ArrayList<String> in place of ArrayIntList to declare a method that takes an ArrayList<String> as a parameter:

        public void doSomethingCool(ArrayList<String> list) {
            ...
        }
It can even be used as a return type if you want to have the method return an ArrayList:

        public ArrayList<String> doSomethingCool(ArrayList<String> list) {
            ...
        }
Once you have declared an ArrayList<String>, you can use manipulate it with the kinds of calls we have made on our ArrayIntList but using Strings instead of ints:

        ArrayList<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("there");
        list.add(0, "fun");
        System.out.println(list);
which produces this output:

        [fun, hello, there]
All of the methods we have seen with ArrayIntList are defined for ArrayList: the appending add, add at an index, remove, size, get, etc. So we could write the following loop to print each String from an ArrayList<String>:

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
I then spent a little time discussing the issue of primitive data versus objects. Even though we can construct an ArrayList<E> for any class E, we can't construct an ArrayList<int> because int is a primitive type, not a class. To get around this problem, Java has a set of classes that are known as "wrapper" classes that "wrap up" primitive values like ints to make them an object. It's very much like taking a candy and putting a wrapper around it. For the case of ints, there is a class known as Integer that can be used to store an individual int. Each Integer object has a single data field: the int that it wrapped up inside.

Java 5 also has quite a bit of support that makes a lot of this invisible to programmers. If you want to put int values into an ArrayList, you have to remember to use the type ArrayList<Integer> rather than ArrayList<int>, but otherwise Java does a lot of things for you. For example, you can construct such a list and add simple int values to it:

        ArrayList<Integer> numbers = new ArrayList<Integer>();
        numbers.add(18);
        numbers.add(34);
In the two calls on add, we are passing simple ints as arguments to something that really requires an Integer. This is okay because Java will automatically "box" the ints for us (i.e., wrap them up in Integer objects). We can also refer to elements of this list and treat them as simple ints, as in:

        int product = numbers.get(0) * numbers.get(1);
The calls on list.get return references to Integer objects and normally you wouldn't be allowed to multiply two objects together. In this case Java automatically "unboxes" the values for you, unwrapping the Integer objects and giving you the ints that are contained inside.

Every primitive type has a corresponding wrapper class: Integer for int, Double for double, Character for char, Boolean for boolean, and so on.

Then I mentioned that I hoped people are aware of the array initializer syntax where you can use curly braces to specify a set of values to use for initializing an array:

        int[] data = {8, 27, 93, 4, 5, 15, 206};
This is a great way to define data to use for a testing program. I asked people how we'd find the product of this list and people suggested the standard approach that uses an int to index the array:

        int product = 1;
        for (int i = 0; i < data.length; i++) {
            product *= data[i];
        }
This approach works, but there is a simpler way to do this. If all you want to do is to iterate over the values of an array one at a time, you can use what is called a for-each loop:

        int product = 1;
        for (int n : data) {
            product *= n;
        }
We generally read the for loop header as, "For each int n in data...". The choice of "n" is arbitrary. It defines a local variable for the loop. I could just as easily have called it "x" or "foo" or "value". Notice that in the for-each loop, I don't have to use any bracket notation. Instead, each time through the loop Java sets the variable n to the next value from the array. I also don't need to test for the length of the array. Java does that for you when you use a for-each loop.

There are some limitations of for-each loops. You can't use them to change the contents of the list. If you assign a value the variable n, you are just changing a local variable inside the loop. It has no effect on the array itself.

As with arrays, we can use a for-each loop for ArrayLists, so we could say:

        ArrayList<String> list = new ArrayList<String>();
        list.add("four");
        list.add("score");
        list.add("and");
        list.add("seven");
        list.add("years");
        list.add("ago");
        for (String s : list) {
            System.out.println(s);
        }
which produces this output:

        four
        score
        and
        seven
        years
        ago

Stuart Reges
Last modified: Tue Apr 6 17:41:57 PDT 2010