CS 5 Spring 2000
Lecture 13
April 17

Arrays

You can think of an array like a table. You give an index and get or store a value. For example, here is a table of recent presidential election winners, indexed by the year of the election:

Year Winner
1948 Truman
1952 Eisenhower
1956 Eisenhower
1960 Kennedy
1964 Johnson
1968 Nixon
1972 Nixon
1976 Carter
1980 Reagan
1984 Reagan
1988 Bush
1992 Clinton

There are a couple of differences between this table and arrays in Java. In Java, array indices always start at 0, and they don't ever skip. So array indices always go as 0, 1, 2, 3, 4, ... . This is different than languages like Pascal, where you can select a lower bound diffent than 0. Indices always start at 0 in C/C++, just as in Java.

Then again, just because indices start at 0, that doesn't mean you always have to use all the entries starting from 0. If you'd rather start from 1, go ahead. The cost is just the wasted space of the 0-indexed entry.

Although our table above has "character strings" (the names of the presidents) as its entries, we can store any type in the array, as long as all entries are the same type. So for starters, our array entries will be something simpler, like integers.

To declare an array of 10 ints you have to do two steps:

  1. Create a variable that can hold a reference to an array of the proper type.
  2. Create the actual array by calling new.
int [] bozo; bozo = new int[10]; This is like objects, and arrays are basically a special kind of object. As with objects, we can combine this into one line: int [] bozo = new int[10]; (Draw the picture of the array, with a reference saved in bozo to a stack of boxes containing the values.)

Here,

The 10 elements of this array are bozo[0], bozo[1], bozo[2], ..., bozo[9]. Note that
An array of N elements has elements indexed from 0 to N-1. An array of N elements does not have an element with index N.
It will soon become natural to start counting from 0.

You can think of array indices as being like subscripts. Our array bozo is like having bozo0, bozo1, bozo2, ... bozo9.

You can make arrays of other sizes and of other types. To make an array blap of 30 doubles:

double [] blap = new double[30];

Array elements can be of any type. Even objects. Even other arrays!

Our first example of an array is in ShortList.java. We have a 10-element array named list. The first for loop assigns the value i+1 to list[i] for i = 0, 1, ..., 9. For example, list[5] is assigned the value 6.

Note how we set up the first for loop:

for (int i = 0; i < N; i++) This will be a very common paradigm for stepping through an N-element array. We start at 0 and continue as long as the index is strictly less than N. Once it hits N, we are out of bounds of the legal array indices, so we must stop. In other words, the loop test i <= N would be wrong, because within the loop body, we would be trying to access list[N], and no such array entry exists. This would cause a runtime exception in Java (although C/C++ would happily make the reference, returning garbage or overwriting other data or code).

The second for loop prints out the values in the array in increasing order of the index. That is, it prints in order list[0], list[1], list[2], up through list[9]. The third for loop prints out the values in decreasing order of the index: list[9], list[8], list[7], down through list[0]. Note how the loop index i decreases in this loop. The fourth for loop prints out the array in decreasing order again, but using a slightly different method. The loop index i increases, but we use a more complex expression, N-i-1, in the array index. When i is 0, N-i-1 is 9. When i is 1, N-i-1 is 8, and so forth. Finally, when i is 9, N-i-1 is 0. This for loop points out that the array index in a program can be any expression that evaluates to an integer in the correct range of indices.

We then show an alternate way to create an array, using an initializer list.

int [] list2 = {1, 3, 5, 9}; Instead of calling new, we put a list of numbers in curly braces on the right side of the assignment statement. The Java compiler counts the number of items in the list, verifies that they are all of type int (how does it know that is the correct type?), and creates an array of that length with the first item assigned to list2[0], the second to list2[1], etc.

Note also that the upper limit of the for loop is list2.length. An array is like a class. It has a special method (subscripting) that uses a different notation than the usual dot notation that most methods used. But when an array is created (via a new or an intializer list) a constant length is created with it. The array length will never change, so it can be a constant, and it is public. Therefore in Java you can always find the number of elements in an array by refering to this constant.

How arrays are stored

You can rely on an array being stored with its entries consecutively in memory. This rule applies only to a single array. There is no guarantee with two different arrays which is stored before the other. There is no guarantee about what preceeds or follows any array in memory.

Because each array is stored consecutively in memory, it makes accessing any element of the array easy for the computer, assuming that it knows

If we know base and eltsize, then the ith element is stored at address
base + (i * eltsize)

This addressing calculation works because array indices start at 0. Suppose instead that they started at 1, which is where you might have expected them to start. Where would the ith element be? Element 1 would be at base, element 2 would be at base + eltsize, element 3 would be at base + (2 * eltsize), and so on. The ith element, therefore, would be at base + ((i-1) * eltsize). Using "0-origin indexing", as is done in Java, allows the simpler address calculation done above.

Applications of arrays

Computing averages

Suppose we want to read in a list of numbers and compute their average. The program AverageList.java gives one way to do that. Actually, the array is not needed for this simple example, but for computing more complicated statistics it is necessary to have all the numbers saved.

This time we don't know in advance how many items we wish to average, so we will use a while loop to read until we find a 0 or we run out of space in the array. We use count to keep track of the number of items in the array, and increment it after assigning each element to a position in the array. (It is after because the array's smallest index is 0.)

To compute the average we use a for loop and keep a running sum in the variable sum. This is a very common way of both running through an array and keeping a running sum.

This process of averaging the entries in an array is done commonly enough that it is worth having a method to do it. The program AverageListMethod.java demonstrates how to do this. Note that the first parameter of the method is called array, and has type double []. We pass the argument list when making the call, and the types match. A copy of the reference (NOT the array) is made. The individual values in the array can therefore be changes, even though the value of list (the place in memory that it refers to) cannot.

Sieve of Eratosthenes

Suppose we wish to find all prime numbers from 2 to N-1. The Sieve of Erastosthenes method goes as follows: We use an array of boolean to store the list. If the array name is prime, then prime[i] indicates whether we believe i to be prime at the moment. The code is in Sieve.java. Initially, all entries indexed 2 to N-1 in prime are true. We then go through the array from left to right, using the variable candidate to index where we are. Upon finding that prime[candidate] is true, we run a nested loop that sets prime[2 * candidate], prime[3 * candedate], prime[4 * candidate], etc. to false. At the end, we print out indices of all array elements containing true; these indices are exactly the primes between 2 and N-1.

How far do we have to go before we are sure that everything left is a prime? N is clearly far enough, but we can do better. As in the prime testing function, sqrt(N) is far enough. Any factor greater than sqrt(N) must be paired with one less than sqrt(N).

Arrays of objects

As I mentioned above, array elements can be any type. For example, objects.

Rect.java defines a class Rect. We saw it before when we looked as interfaces. I have added the method shrink(), which shrinks the rectangle by the same number of pixels in the x and y directions. There are also other added methods, which you can see in the code.

RectArray.java uses this to draw a pattern. The instance variable box is defined to be a reference to an array of N Rect objects. Note that this declaration:

Rect [] box = new Rect[N]; does NOT create any Rect objects, only boxes that can point to them. (Draw picture.)

The init() function actually creates these objects in a for loop. In other words, for i = 0, 1, ..., 5, the array entry box[i] is a reference to a Rect object. Therefore, we can call member functions of the Rect class on it. For example, we have loops that say

for (int i = 0; i < N; i++) box[i].fill(page); and for (int i = 0; i < N; i++) { box[i].shrink(5); box[i].setColor(Color.red); box[i].fill(page); } Note that there are two levels of references: the box instance variable contains a reference to and array, and each entry in the array is a reference to a Rect object.
Scot Drysdale <scot@cs.dartmouth.edu>
Last modified: Sunday, April 16, 2000