The Numbers Examples: Part 1

Storing numbers in an array

We have looked at reading a series of numbers, adding them together and keeping a count of the number read as we go. However, that did not enable us to remember the numbers read. Once the next number was read, the store used for the old number was overwritten since each number was read into the same variable (n).

The simplest way of reading a sequence of numbers and storing them so we can access them individually again is to use an array. The Java declaration

int[] a; 
declares variable a to be of type array of ints, or another way of putting it, int is the base type of array a. All the components of an array must be of the same base type, and each may be accessed individually using an index. Indexing in Java (like C) always starts at 0, so a is made up of separate parts a[0], a[1] and so on, each of which may be considered a separate int variable. A lot of the power of arrays comes from the ability to use index expressions with them, in other words any Java expression which evaluates to an integer may occur between the [ and ] and is evaluated as the program is run. For example, if we have variable i of type int, a[i] refers to the section of a indexed by whatever value is in variable i at the time.

Note that in Java (and here it differs from C) the declaration of an array variable does not reserve computer store for an array. That must be done separately through a new statement. So once we have the previous declaration int[] a, to actually make an array of ten ints, we need the command

a = new int [10];
The ints in a are then a[0], a[1] and so on up to a[9]. It is, however, possible to combine the array declaration and creation in one statement using the declaration with initialisation capability of Java, so in this case that would be:
int [] a = new int [10];
As a simple example, here is a program which just reads in a list of numbers, storing them in an array, and then prints them out:
 1  import java.io.*;
 2
 3  class Numbers0
 4  {
 5  //  Read a number of integers and store them in an array.
 6
 7   public static void main (String[] args) throws IOException
 8   {
 9    int [] data;
10    int number,count;
11    BufferedReader in = Text.open(System.in);
12    System.out.print("Type the number of integers that will be entered: ");
13    number=Text.readInt(in);
14    data = new int[number];
15    for(count=0; count<number; count++)
16       {
17        System.out.print("Enter number "+(count+1)+": ");
18        data[count]=Text.readInt(in);
19       }
20    System.out.println("The numbers entered were:");
21    for(count=0;count<number;count++)
22       System.out.print(data[count]+" ");
23    System.out.println();
24   }
25  }
Here the array is called data It is declared on line 9, but created on line 14. In this case its later creation means we can use the number read in, and stored in variable number to indicate the size of array to create. The variable count used in the loop on lines 15-19 has its value increased by one each time round the loop, so each time round the loop, data[count] refers to a new store location within data. In the loop on lines 21-22, count again cycles in value from 0 to the point where it equals the value in number and hence the loop is exited, this time it is used to print out all the integers stored in array data.

Note that this example, along with the other examples on this web page, and some further examples of programs with arrays of integers may be found in the directory

/import/teaching/BSc/1st/ItP/numbers

Array Operations

We can now build on the program in Numbers0 and develop a few operations on arrays of numbers. Here is a program which, like Numbers0 reads a sequence of numbers, stores them in an array and prints them out. But it also find the average, the biggest and the smallest numbers.
 1  import java.io.*;
 2
 3  class Numbers1
 4  {
 5  //  Read a number of integers and store them in an array.
 6  //  Find and print the biggest and smallest number and the average.
 7
 8   public static void main (String[] args) throws IOException
 9   {
10    int [] data;
11    int number;
12    double average;
13    BufferedReader in = Text.open(System.in);
14    System.out.print("Type the number of integers that will be entered: ");
15    number=Text.readInt(in);
16    data=NumberOps0.readall(number,in);
17    System.out.println("The numbers entered were:");
18    NumberOps0.printall(data);
19    System.out.println("The biggest is: "+NumberOps0.biggest(data));
20    System.out.println("The smallest is: "+NumberOps0.smallest(data));
21    System.out.print("The average is: ");
22    average=NumberOps0.average(data);
23    System.out.println(Text.writeDouble(average,5,3));
24   }
25  }
The program doesn't look much, and the reason for that is that most of the real work has been put into a separate class, called NumberOps0. This class has a collection of static methods which do the operations we are interested in. Here it is:
 1  import java.io.*;
 2
 3  class NumberOps0
 4  {
 5
 6   public static int[] readall(int n,BufferedReader reader)
 7   throws IOException
 8   // Create an array of n integers, read them from reader
 9   {
10    int count;
11    int [] a = new int[n];
12    for(count=0; count<n; count++)
13       {
14        System.out.print("Enter number "+(count+1)+": ");
15        a[count]=Text.readInt(reader);
16       }
17    return a;
18   }
19
20   public static void printall(int [] a)
21   // Print the contents of array a
22   {
23    int i;
24    for(i=0; i<a.length; i++)
25       System.out.print(a[i]+" ");
26    System.out.println();
27   }
28 
29   public static int biggest(int [] a)
30   // return the biggest number in array a
31   {
32    int i,current=a[0];
33    for(i=1; i<a.length; i++)
34       if(a[i]>current)
35          current=a[i];
36    return current;
37   }
38
39   public static int smallest(int [] a)
40   // return the smallest number in array a
41   {
42    int i,current=a[0];
43    for(i=1; i<a.length; i++)
44       if(a[i]<current)
45          current=a[i];
46    return current;
47   }
48
49   public static double average(int [] a)
50   // return the average of the numbers in array a
51   {
52    int i,sum=0;
53    for(i=0; i<a.length; i++)
54       sum+=a[i];
55    return (double)sum/a.length;
56   }
57
58 }
As you can see, the program is broken up into small fragments, each of which are self-contained, with the main method just acting as a framework bringing them together. Breaking programs up into small parts like this is recommended as a way of keeping them easy to read, understand and modify.

The first method in NumberOps0 is the most complex one. However, all its is really doing is taking the code from lines 14-19 of Numbers0 and making a separate method out of it. The method resulting, called readall can then be re-used whenever it is required to read a sequence of numbers and store them in an array which is created for the purpose, just by making a method call similar to that in line 16 of Numbers1. Note that readall takes as an argument not only the number of integers that are to be read, but also the reader from which they are to be read. Internally in readall the reader is called reader, but the call on line 16 of Numbers1 means this local variable is instantiated to the BufferedReader value in variable in there, which line 13 in Numbers1 set to a reader of characters typed into the command window. The method readall returns the array it has created and filled with the numbers read, so its return type is int[]. So note it is perfectly possible in Java to have an array type as a return type. The command on line 16 of Numbers1 means the array referred to locally as a in readall is assigned to the variable data in Numbers1.

As readall in NumberOps0 makes use of BufferedReader from the java.io package it is necessary to have the import statement on line 1 of NumberOps0. Also, since Text.readInt on line 15 of NumberOps0 can throw the checked exception IOException, as this exception is not caught in readall it is necessary to indicate, as is done on line 7, that readall may throw an IOException.

The method printall on lines 20-27 of NumberOps0 turns what was done on lines 21-23 of Numbers0 into a separate method. As this method does not return any value, but instead has a side-effect (i.e. it prints the numbers in the array), it has return type void, and the call to it on line 18 of Numbers1 forms a statement of its own. One difference between printall and lines 21-23 of Numbers0 is that printall does not have a separate variable holding the size (i.e. number of elements) of the array. However, the size of any array a is given by the integer value a.length, and this is used in printall on line 24.

The biggest and smallest methods in NumberOps0 both work by having a variable called current, initially set to the first item in the array (i.e. the one indexed by 0). The remaining integers in the array are then looked at in turn, replacing current by any bigger/smaller integer found. So at any point in the execution of these methods the variable current hold the value of the biggest/smallest integer stored in that portion of the array which has been looked at so far. When the whole array has been considered, and the loop is exited with control passing to line 36 or 46, current must hold the value of the biggest/smallest integer in the whole array. So the value in current is returned as the output of the method. As these methods return a value, the calls to them, on lines 19 and 20 of Numbers1 occur in the place where the value is used.

The average runs through the array, adding together all the numbers in it in the variable sum. At any point in the execution of average, the variable sum must hold the sum of the integers in that portion of the array that has been looked at. When the whole array has been considered and the loop is exited with control passing to line 55, sum must hold the sum of all the integers in the array. Note that as in the Average examples, the average is calculated using floating point division so that it can be quoted to several decimal places. This means the return type of average is double, with the division of the sum of the integers by the number of integers being done within the average method.

It is possible to write a program which performs similarly to Numbers1 but does not divide the code up into separate parts. For comparison, such a program is given below:

 1  import java.io.*;
 2
 3  class Numbers1a
 4  {
 5
 6  //  Read a number of integers and store them in an array.
 7  //  Find and print the biggest and smallest number and the average.
 8  //  Program deliberately written as a single method.
 9
10   public static void main (String[] args) throws IOException
11   {
12    int [] data;
13    int number,count;
14    int currentBiggest,currentSmallest,sum;
15    double average;
16    BufferedReader in = Text.open(System.in);
17    System.out.print("Type the number of integers that will be entered: ");
18    number=Text.readInt(in);
19    data = new int[number];
20    for(count=0; count<number; count++)
21       {
22        System.out.print("Enter number "+(count+1)+": ");
23        data[count]=Text.readInt(in);
24       }
25    System.out.println("The numbers entered were: "+data[0]+" ");
26    currentBiggest=data[0];
27    currentSmallest=data[0];
28    sum=data[0];
29    for(count=1;count<number;count++)
30       {
31        System.out.print(data[count]+" ");
32        sum+=data[count];
33        if(data[count]>currentBiggest)
34           currentBiggest=data[count];
35        else if(data[count]<currentSmallest)
36           currentSmallest=data[count];
37       }
38    System.out.println();
39    System.out.println("The biggest is: "+currentBiggest);
40    System.out.println("The smallest is: "+currentSmallest);
41    average=(double)sum/number;
42    System.out.println("The average is: "+Text.writeDouble(average,5,3));
43   }
44  }
This program has the minor advantage of being slightly more efficient as after reading it in, it only goes through the array once (on lines 29-37), doing the operations of printing, summing and checking for being bigger or smaller than the current biggest or smallest all in one go with each integer. However, because it is not broken up into separate parts with separate functions, it is not so easy to see what it does. Additionally, it is not possible to re-use the components of this program, whereas the methods in NumberOps0 may be re-used whenever it is required to perform the relevant operation on an array.

Fixed-sized arrays, varying numbers of contents

The previous programs worked because we knew in advance how many numbers were going to be entered. However, suppose we did not. Since arrays are of a fixed size which is given when they are created, if we were reading numbers into an array and we didn't know how many numbers there were going to be, we would have to create an array of the maximum size we thought we might need, and keep a record of the actual number of integers read elsewhere. This technique is used in the program below. It uses the notion of a sentinel value to indicate "end of data" which we used in some of the "Averages" programs:
 1  import java.io.*;
 2
 3  class Numbers2
 4  {
 5
 6  //  Read a number of integers and store them in an array.
 7  //  Find and print the biggest and smallest number and the average.
 8
 9  static final int SENTINEL = -999;
10  static final int MAXNUMS = 100;
11
12   public static void main (String[] args) throws IOException
13   {
14    int [] data;
15    int count,n;
16    double average;
17    BufferedReader in = Text.open(System.in);
18    data = new int[MAXNUMS];
19    for(count=0; ;count++)
20       {
21        System.out.print("Enter number "+(count+1)+" (or ");
22        System.out.print(SENTINEL+" to finish): ");
23        n=Text.readInt(in);
24        if(n==SENTINEL)
25           break;
26        data[count]=n;
27       }
28    System.out.println("The numbers entered were:");
29    NumberOps.printall(data,count);
30    System.out.println("The biggest is: "+NumberOps.biggest(data,count));
31    System.out.println("The smallest is: "+NumberOps.smallest(data,count));
32    System.out.print("The position of the biggest is: ");
33    System.out.println(NumberOps.posBiggest(data,count)+1);
34    System.out.print("The position of the smallest is: ");
35    System.out.println(NumberOps.posSmallest(data,count)+1);
36    System.out.print("The average is: ");
37    average=NumberOps.average(data,count);
38    System.out.println(Text.writeDouble(average,5,3));
39   }
40  }
In this case the idea of having a separate method to read the array is abandoned. Part of the problem is that if there were to be such a method it would really need to return both the array itself and the number of integers read, but a method can only return one value. We will see ways of dealing with this later. Note that the maximum number of items in the array is given as a constant, declared on line 10. The operations on the array are similar to those of before, but they have to take an extra argument giving the number of integers in the array that are actually being dealt with. The code for them is given below:
 1  class NumberOps
 2  {
 3  // Operations on an array of integers a,
 4  // dealing with the first n integers only.
 5
 6   public static void printall(int [] a,int n)
 7   // Print the first n contents of array a
 8   {
 9    int i;
10    for(i=0; i<n; i++)
11       System.out.print(a[i]+" ");
12    System.out.println();
13    }
14
15   public static int biggest(int [] a,int n)
16   // find the biggest of the first n contents of array a
17   {
18    int i,current=a[0];
19    for(i=1; i<n; i++)
20      if(a[i]>current)
21         current=a[i];
22    return current;
23   }
24
25   public static int smallest(int [] a,int n)
26   // find the smallest of the first n contents of array a
27   {
28    int i,current=a[0];
29    for(i=1; i<n; i++)
30       if(a[i]<current)
31          current=a[i];
32    return current;
33   }
34
35   public static int posBiggest(int [] a,int n)
36   // find the position of the biggest of the first n contents of array a
37   {
38    int i,pos=0;
39    for(i=1; i<n; i++)
40       if(a[i]>a[pos])
41          pos=i;
42    return pos;
43   }
44
45   public static int posSmallest(int [] a,int n)
46   // find the position of the smallest of the first n contents of array a
47   {
48    int i,pos=0;
49    for(i=1; i<n; i++)
50       if(a[i]>a[pos])
51          pos=i;
52    return pos;
53   }
54
55   public static double average(int [] a,int n)
56   // find the average of the first n contents of array a
57   {
58    int i,sum=0;
59    for(i=0; i<n; i++)
60       sum+=a[i];
61    return (double)sum/n;
62   }
63  }
In this program we have included two extra methods posBiggest and posSmallest which give the array index of the biggest and smallest elements. It is important to distinguish these from the actual values of the biggest and smallest elements - a common source of errors in programs dealing with arrays is failing to note this distinction between a value and an index to where the value is stored in an array. Note that in the actual output of the program, 1 is added to the output of these index methods, to reflect the fact that in Java what we would think of as the "first" element in an array is indexed by 0, what we would think of as the "second" element is indexed by 1 and so on.

Note that attempting to access an element of an array by using an index which goes beyond the size of the array as it was created causes an exception of the type ArrayIndexOutOfBoundsException. This is a run-time exception, so it did not have to be signalled with a throws statement. However, it can be caught if a try statement is written to catch it. Here is a program similar to the above which uses a try statement to catch the use of an array index beyond the bounds of the array:

 1  import java.io.*;
 2
 3  class Numbers3
 4  {
 5
 6  //  Read a number of integers and store them in an array.
 7  //  Find and print the biggest and smallest number and the average.
 8
 9  static final int SENTINEL = -999;
10  static final int MAXNUMS = 10;
11
12   public static void main (String[] args) throws IOException
13   {
14    int [] data;
15    int count=0,n;
16    double average;
17    BufferedReader in = Text.open(System.in);
18    data = new int[MAXNUMS];
19    try
20       {
21        for(count=0; ;count++)
22           {
23            System.out.print("Enter number "+(count+1)+" (or ");
24            System.out.print(SENTINEL+" to finish): ");
25            n=Text.readInt(in);
26            if(n==SENTINEL)
27               break;
28            data[count]=n;
29           }
30       }
31    catch(ArrayIndexOutOfBoundsException e)
32       {
33        System.out.println("Cannot take more than "+MAXNUMS+" numbers");
34       }
35    System.out.println("The numbers entered were:");
36    NumberOps.printall(data,count);
37    System.out.println("The biggest is: "+NumberOps.biggest(data,count));
38    System.out.println("The smallest is: "+NumberOps.smallest(data,count));
39    System.out.print("The average is: ");
40    average=NumberOps.average(data,count);
41    System.out.println(Text.writeDouble(average,5,3));
42   }
43  }
Here, if during the execution of lines 21-29 an attempt is made to index an array using an index beyond its bounds, execution goes to line 33 and prints out the error message there. In fact this use of a out-of-bounds index can only occur if an attempt is made to read more numbers than the size of array, which will occur at line 28. As the for loop which starts at line 21 has an empty condition part there, it can only be exited by such an exception or by the sentinel being read, causing a break initiated at line 27. In both cases, the next step is to execute line 35 and subsequent statements. The size of the array has been reduced to 10 by changing the constant declaration on line 10, thus enabling the effect to be easily demonstrated.

Note that there is no need to write another file of methods here, as the methods we have already used in NumberOps are used in this Numbers3 class as well. We save effort by reusing them.


Matthew Huntbach

These notes were produced as part of the course Introduction to Programming as it was given in the Department of Computer Science at Queen Mary, University of London during the academic years 1998-2001.

Last modified: 6 Oct 1998