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);
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:
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
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:
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.