CSE143 Notes for Wednesday, 11/30/11

I said that I wanted to revisit our discussion of the ArrayIntList class. First I wrote some code to construct an ArrayIntList and to fill it up with values:

        public class ArrayIntListExample {
            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);
I said that I wanted to explore how we could use it with an iterator. We examined iterators when we looked at collections like sets earlier in the quarter. You can also use iterators for lists. Normally an iterator like this would be declared as being of type Iterator<Integer>. We'll see how to do that next week. But for now I said that I wanted to write a class called ArrayIntListIterator. So using an iterator, we would rewrite this as:
	ArrayIntListIterator i = list.iterator();
	int sum = 0;
	while (i.hasNext()) {
	    int next = i.next();
	    sum += next;
	}
	System.out.println("sum = " + sum);
Remember 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 finds the sum and removes all of the occurrences of the value 3:

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

Then we spent some time discussing how the ArrayIntListIterator is implemented. The main function the iterator performs is to keep track of a particular position in a list, so the primary field will be an integer variable for storing this position:

        public class ArrayIntListIterator {
            private int position;

            public ArrayIntListIterator(?) {
                position = 0;
            }

            public int next() {
                position++;
            }

            ...
        }
I asked people how we would implement hasNext and someone said we'd have to compare the position against the size of the list. I then said, "What list?" Obviously the iterator also needs to keep track of which list it is iterating over. We can provide this information in the constructor for the iterator. So the basic outline became:

        public class ArrayIntListIterator {
            private ArrayIntList list;
            private int position;

            public ArrayIntListIterator(ArrayIntList list) {
                position = 0;
                this.list = list;
            }

            public int next() {
                use get method of list & position
                position++;
            }

            public boolean hasNext() {
                check position against size
            }

            ...
        }
We briefly discussed how to implement remove. We have to keep track of when it's legal to remove a value. Recall that you can't remove before you have called get and you can't call remove twice in a row. We decided that this could be implemented with a boolean flag inside the iterator that would keep track of whether or not it is legal to remove at any given point in time. Using this flag, we can throw an exception in remove if it is not legal to remove at that point in time:

        public class ArrayIntListIterator {
            private ArrayIntList list;
            private int position;
            private boolean removeOK;
        
            public ArrayIntListIterator(ArrayIntList list) {
                position = 0;
                this.list = list;
                removeOK = false;
            }
        
            public int next() {
                use get method of list & position
                position++
                removeOK = true;
            }
        
            public boolean hasNext() {
                check position against size
            }
        
            public void remove() {
                if (!removeOK)
                    throw new IllegalStateException()
                call remove method on list
                removeOK = false;
            }
        }
This is a fairly complete sketch of the ArrayIntListIterator code.

Then I discussed the fact that the new version of the list "grows" the list as needed if it runs out of capacity. It isn't, in general, easy to make an array bigger. We can't simply grab the memory next to it because that memory is probably being used by some other object. Instead, we have to allocate a brand new array and copy values from the old array to the new array. This is pretty close to how shops and other businesses work in the real world. If you need some extra space for your store, you can't generally break down the wall and grab some of the space from the store next door. More often a store has to relocate to a larger space.

The new version of ArrayIntList has this functionality built into it. In the previous version we had a checkCapacity method that throws an exception if the array isn't big enough. In the new version that has been replaced by an ensureCapacity method that constructs a new array if necessary.

Obviously you don't want to construct a new array too often. For example, suppose you had space for 1000 values and found you needed space for one more. You could allocate a new array of length 1001 and copy the 1000 values over. Then if you find you need space for one more, you could make an array that is 1002 in length and copy the 1001 old values over. This kind of growth policy would be very expensive.

Instead, we do something like doubling the size of the array when we run out of space. So if we have filled up an array of length 1000, we double its size to 2000 when the client adds something more. That makes that particular add expensive in that it has to copy 1000 values from the old array to the new array. But it means we won't need to copy again for a while. How long? We can add another 999 times before we'd need extra space. As a result, we think of the expense as being spread out or "amortized" over all 1000 adds. Spread out over 1000 adds, the cost is fairly low (a constant).

You will find that the built-in ArrayList class does something similar. The documentation is a little coy about this saying, "The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost." If you look at the actual code, you'll find that increase by 50% each time (a multiplier of 1.5).

The latest version of the ArrayIntList class along with the ArrayIntListIterator class are included in the calendar for this lecture.

I then discussed an example of writing a Graphical User Interaface (GUI) in java. The resulting code is available from the calendar.


Stuart Reges
Last modified: Thu Dec 1 10:05:40 PST 2011