Loops revision - full array traversal

If you are confident with using loops to manipulate arrays, you can skip this section of notes. It covers topics you have already covered in your first programming course, which are particularly relevant to this course on algorithms and data structures. If, however, you are still unsure over basic loops with arrays, please read on.

In a previous set of notes you saw a simple program that read a list of numbers, stored them in an array, and then counted the number of times a particular number occurred in the list. You were also reminded that for the purpose of this course, the important part of the code was the bit that went through the array counting the occurrences of the number given. This was written:

   count=0;
for(int i=0; i<a.length; j++)
if(a[i]==n)
count++;
We call this an array traversal since it goes through all the elements of an array. We have added here the initialisation of the variable count to 0. If we couldn't be sure that count was initially set to 0, we wouldn't be able to say that after the execution of the code it is set to the number of times the integer in n occurs in the array referred to be a.

In the next set of notes you saw how it made sense to put this code into a static method:

  public static count(int n,int[] a)
{
int count=0;
for(int i=0; i<a.length; i++)
if(a[i]==n)
count++;
return count;
}
This method can then be called whenever we want to perform the task that the code in the method performs, in this case counting the number of times a given integer occurs in an array of integers. A static method is self-contained: you can view it as a mini program which takes its input from the arguments in the header, and gives its output by the return command. But please distinguish between these usages of the terms "input" and "output" which refer to an internal process in the execution of a program, and the alternative use of "input" and "output" to mean the program having an external effect, reading or writing something either to some form of display, or to a file which persists once program execution has finished.

It doesn't really matter what the variables are called in a small piece of code like this, although it's good to stick to conventions. The variable name i, and also j and k if necessary, are often used for a variable which is used as an index to run through an array. The variable name n is often used for an integer whose value stays fixed, and the variable name a for an array. Note that in Java the convention is that variable names always begin with small letters. Although you could have, for example, a variable called N, you wouldn't in general. Also note Java is a "case sensitive" language, which means that changing a letter from a small letter to a capital letter in a word make it a different word. So n would be a separate variable from N. Java keywords are always written with small letters, so if you wanted to write a for-loop, for example, you could not start to with For. Also, please note that you should where possible use meaningful variable names. One letter variable names are fine for short general purpose methods, but do not make a habit of using them in any cases where a more meaningful variable name would better illustrate the purpose of a piece of code.

Looping through an array

Now I want to focus on the actual task itself. A good way of thinking of it is as just one version of a more general task of going through the elements of the array doing something with each of them. We could write this as:
   for(int i=0; i<a.length; j++)
dosomething
The dosomething here could be any Java statement, remembering that a whole list of statements can be made into one statement by starting it with { and ending it with }. It needs to do something with the expression a[i]. Although a[i] can be considered a variable, since i itself is a variable, the actual store location it refers to varies as i varies. The first time round the loop, i is set to 0, so a[i] refers to a[0], the second time round the loop, i is set to 1, so a[i] refers to a[1] and so on. The only time this wouldn't be true would be if dosomething could change the value of i, which would lead to some very confusing code, so should generally be avoided.

We could represent this general pattern diagrammatically by:

where the inner box is filled by code which does something with a[i], and the whole effect is to do something with a[0], then to do something with a[1], then to do something with a[2], and so on right through the array up till its last element. Since for any array a, the number of elements in the array is given by a.length, and counting starts at 0, the last element is given by a[a.length-1].

In the example which we have been discussing, dosomething is, expressed in English "check if a[i] is equal to n, and if it is, add 1 to count". We can express that in Java:

    if(a[i]==n)
count++;
By setting dosomething to something else, we can produce all sorts of useful pieces of code, each of which takes the general pattern of doing something with all the elements of an array one at a time. For example, dosomething could be, expressed in English "add a[i] to the value of count". In Java, that would be:
   count = count+a[i];
although a shorthand version of it which means the same is count+=a[i];. It is a matter of opinion whether you think these shorthand versions, which Java carries over from its ancestor, the language C, lead to clearer programs or not. Anyhow, putting this in the loop, with count initialised to 0 gives us:
   int count=0;
for(int i=0; i<a.length; i++)
count = count+a[i];
which is a piece of code whose overall effect is to add together all the integers in the array a and leave the result in the variable count. As another example, suppose we had a BufferedReader variable called in, then making dosomething the statement:
   {
System.out.print("Enter number for position "+i+": ");
String line = in.readLine();
a[i] = Integer.parseInt(line);
}
we would get code that prompts for new values and puts them into an array. Adding code to initialise in first makes it:
  BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for(int i=0; i<a.length; i++)
{
System.out.print("Enter number for position "+i+": ");
String line = in.readLine();
a[i] = Integer.parseInt(line);
}

Another variation might be to find the biggest integer in an array. Suppose we have some integer variable called big. Then our dosomething would be in English: "check if a[i] is greater than the value in big, and if it is, copy a[i] into big". In Java this is:

   if(a[i]<big)
big=a[i];
At the end of this, the variable big will hold the biggest integer in the array. So this makes the code:
  for(int i=0; i<a.length; i++)
if(a[i]>big)
big=a[i];
In this case, however, what do we initialise big to? You might think 0, but suppose all the integers in the array are negative? The answer given would then be 0. We can overcome this by setting big to the first element in the array:
  int big=a[0];
for(int i=0; i<a.length; i++)
if(a[i]>big)
big=a[i];
But then there is really no point in starting going through the array with a[0], so we have a variation on the pattern which goes through all elements of the array except the first one:
  int big=a[0];
for(int i=1; i<a.length; i++)
if(a[i]>big)
big=a[i];
To go through all the elements of the array except the first one, we start off with the loop index, i, set to 1 rather than 0.

We could use this as the basis of a static method which takes an array and returns the biggest number in the array:

  public static int biggest(int[] a)
{
int big=a[0];
for(int i=1; i<a.length; i++)
if(a[i]>big)
big=a[i];
return big;
}

Changing an array

Let us now consider some code that goes through an array changing it. A simple example might be to add the integer stored in variable n to every integer stored in an array. Using the pattern we have established, this can be done by setting dosomething to a[i]+=n;, making the whole loop:
  for(int i=1; i<a.length; i++)
a[i]+=n;
Here is a complete program which demonstrates this piece of code:
import java.util.*;
import java.io.*;

class AddNumber1
// A program to read a list of numbers into an array then read one number and
// add that number to every number in the array.
{
public static void main(String[] args) throws Exception
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a list of numbers (all on one line):");
String line = in.readLine();
StringTokenizer toks = new StringTokenizer(line);
int[] a = new int[toks.countTokens()];
for(int i=0; i<a.length; i++)
a[i] = Integer.parseInt(toks.nextToken());
System.out.println("The numbers stored are:");
for(int i=0; i<a.length; i++)
System.out.print(a[i]+" ");
System.out.println();
System.out.print("Enter a number: ");
line = in.readLine();
int n = Integer.parseInt(line);
for(int i=0; i<a.length; i++)
a[i]+=n;
System.out.println("Adding that number to the numbers stored gives:");
for(int i=0; i<a.length; i++)
System.out.print(a[i]+" ");
System.out.println();
}
}
As you saw in the previous set of notes, a neater program can be obtained by breaking up code into separate methods. In this case, that gives us:
import java.util.*;
import java.io.*;

class AddNumber2
// A program to read a list of numbers into an array then read one number and
// add that number to every number in the array.
{
public static void main(String[] args) throws Exception
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a list of numbers (all on one line):");
int[] a = readNumbers(in);
System.out.println("The numbers stored are:");
writeNumbers(a);
System.out.print("Enter a number: ");
int n = readANumber(in);
addNumber(n,a);
System.out.println("Adding that number to the numbers stored gives:");
writeNumbers(a);
}

public static int[] readNumbers(BufferedReader in) throws Exception
{
String line = in.readLine();
StringTokenizer toks = new StringTokenizer(line);
int[] a = new int[toks.countTokens()];
for(int i=0; i<a.length; i++)
a[i] = Integer.parseInt(toks.nextToken());
return a;
}

public static int readANumber(BufferedReader in) throws Exception
{
String line = in.readLine();
int n = Integer.parseInt(line);
return n;
}

public static void writeNumbers(int[] a)
{
for(int i=0; i<a.length; i++)
System.out.print(a[i]+" ");
System.out.println();
}

public static void addNumber(int n,int[] b)
{
for(int i=0; i<b.length; i++)
b[i]+=n;
}

}
Note how introducing the method writeNumbers means the code in that method is re-used for the two times we need to write the contents of the array.

The method writeNumbers is one example of a void-returning method, it doesn't return any value because its only effect is the effect which is external to the program of displaying the numbers in the array it takes as its parameter. However, the method addNumber is also void-returning. Note how as these methods do not return a value, their call forms a separate statement on its own, whereas calls to methods that return values form just part of statements which do something with those values, for example, assign them to a variable.

The method addNumber does not have an external effect, but it works by changing the value of its parameter. Recall that if an integer parameter variable is assigned a new value in a method, that will not change the value of the variable that was matched to it when the method was called. Actually, if any parameter variable is assigned a new value, that will not change the value of the variable that was matched to it when the method was called. But if part of the value of a structure a parameter variable refers to is changed in the method, then that change will change the value of the structure referred to by the variable which matched the parameter in the method call. In the above example, you will see that the method call addNumber(n,a) causes the argument a to be matched against the parameter b in the method. Changes to the content of cells of the array referred to by b in the method will change the cells of the array called a in the main method where the method addNumber was called. The reason for this is that a in main and b in changeNumber actually refer to the same array. When arrays are passed as parameters, they are not copied, rather the local reference in the method, even if it has another name, is just a local name for the same array. This is, perhaps, a tricky point to grasp, and we will return to it with a more detailed explanation later in this course.

Because the method changeNumber here works by changing and hence destroying the old value of the array passed to it as a parameter, it is known as a destructive method. An alternative way of doing the work would be to create a new array which has the desired change to the old array while keeping the old array unchanged. Such a method is referred to as a constructive method. Since the length of array a is a.length we can create an array of the integers of the same length by new int[a.length]. This is used below in a variant of our pattern of going through every element of an array, this time going through every element of the array a using it to give a new value to the equivalent element of array b:

  int [] b = new int[a.length];
for(int i=0; i<a.length; i++)
b[i] = a[i]+n;
Before the loop we declared a name for a new integer array variable and set it to refer to an array of integers of the same length as a. The result of this code is that b refers to an array which represents the effect of changing the array referred to be a by ading the value in variable n to each of its elements. But the array referred to by a has not itself been changed.

We can demonstrate this in a complete program:

import java.util.*;
import java.io.*;

class AddNumber3
// A program to read a list of numbers into an array then read one number and
// add that number to every number in the array, done constructively.
{
public static void main(String[] args) throws Exception
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a list of numbers (all on one line):");
int[] a = readNumbers(in);
System.out.println("The numbers stored are:");
writeNumbers(a);
System.out.print("Enter a number: ");
int n = readANumber(in);
int[] a1 = addNumber(n,a);
System.out.println("Adding that number to the numbers stored gives:");
writeNumbers(a1);
System.out.println("The original numbers are:");
writeNumbers(a);
}

public static int[] readNumbers(BufferedReader in) throws Exception
{
String line = in.readLine();
StringTokenizer toks = new StringTokenizer(line);
int[] a = new int[toks.countTokens()];
for(int i=0; i<a.length; i++)
a[i] = Integer.parseInt(toks.nextToken());
return a;
}

public static int readANumber(BufferedReader in) throws Exception
{
String line = in.readLine();
int n = Integer.parseInt(line);
return n;
}

public static void writeNumbers(int[] a)
{
for(int i=0; i<a.length; i++)
System.out.print(a[i]+" ");
System.out.println();
}

public static int[] addNumber(int n,int[] a)
{
int[] b = new int[a.length];
for(int i=0; i<a.length; i++)
b[i] = a[i]+n;
return b;
}
}
The version of addNumber here has return type int[] and does not change the array passed as a parameter.

In general, when writing methods that deals with arrays, you should make sure that either your methods are destructive, which is indicated by them returning void, or they are constructive and make no changes to the array passed as a parameter. Calling a method that returns a new array but also changes the array passed as a parameter could result in a programming error if it wasn't realised that the method changed its parameter. This falls into the general guideline of not mixing up several things in a method, in this case both returning a value and changing a value. A common mistake I see is for people, when asked to write a constructive method, to write one which returns a new value, but is also destructive because it changes the value passed as a parameter. Below is an example:

 public static int[] addNumber(int n,int[] a)
// Given deliberately as an example of bad method design
{
int[] b = new int[a.length];
for(int i=0; i<a.length; i++)
a[i] = a[i]+n;
return a;
}
There is no point in writing a method like this, since there is no point in it returning a value. The value returned is simply the same as the value of the argument after the method returned.

Arrays of things other than integers

Arrays of integers are so often given to illustrate simple array programming, that I sometimes find people who suppose that the type int[] is the type for an array. In fact arrays of any type may be used, so if X is a type, then X[] is the type "array of X". For example, String[] is the type "array of strings". There is no type which is just "array", since an array must have a base type.

To illustrate this, let us consider a program which is very close to our program which reads some numbers, stores them in an array, and then adds a number to each of them. In this case, we have a program which reads some strings, stores them in an array, and then appends a string to each of them:

import java.util.*;
import java.io.*;

class AddWord
// A program to read a list of words into an array then read one word and
// append that word to every word in the array.
{
public static void main(String[] args) throws Exception
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a list of words (all on one line):");
String[] a = readWords(in);
System.out.println("The words stored are:");
writeWords(a);
System.out.print("Enter a word: ");
String w = in.readLine();
addWord(w,a);
System.out.println("Appending that word to the words stored gives:");
writeWords(a);
}

public static String[] readWords(BufferedReader in) throws Exception
{
String line = in.readLine();
StringTokenizer toks = new StringTokenizer(line);
String[] a = new String[toks.countTokens()];
for(int i=0; i<a.length; i++)
a[i] = toks.nextToken();
return a;
}

public static void writeWords(String[] a)
{
for(int i=0; i<a.length; i++)
System.out.print(a[i]+" ");
System.out.println();
}

public static void addWord(String w,String[] a)
{
for(int i=0; i<a.length; i++)
a[i]+=w;
}

}
As you can see, the method addWord is almost identical to the addNumber given previously. The types of the arguments are different, since the method takes a string and an array of strings, rather than an integer and an array of integers. Of course, the index variable i which is used to go through the array, is still an integer. Note that as Java allows us to use the + symbol to join two strings, it also allows us to use += to change what a string variable refers to. str1+=str2 where str1 and str2 are of type String is still a shorthand for str1=str1+str2, and can be thought of as meaning "change what str1 refers to by appending str2 to the rear of it".

Please note, all the code referred to in these notes can be found in the directory referenced here.


Matthew Huntbach

Last modified: 14 December 2004