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;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
for(int i=0; i<a.length; j++)
if(a[i]==n)
count++;
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)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
{
int count=0;
for(int i=0; i<a.length; i++)
if(a[i]==n)
count++;
return count;
}
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.
for(int i=0; i<a.length; j++)The
dosomething
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)By setting
count++;
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;which is a piece of code whose overall effect is to add together all the integers in the array
for(int i=0; i<a.length; i++)
count = count+a[i];
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:
{we would get code that prompts for new values and puts them into an array. Adding code to initialise
System.out.print("Enter number for position "+i+": ");
String line = in.readLine();
a[i] = Integer.parseInt(line);
}
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)At the end of this, the variable
big=a[i];
big
will hold the
biggest integer in the array. So this makes the code:
for(int i=0; i<a.length; i++)In this case, however, what do we initialise
if(a[i]>big)
big=a[i];
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];But then there is really no point in starting going through the array with
for(int i=0; i<a.length; i++)
if(a[i]>big)
big=a[i];
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];To go through all the elements of the array except the first one, we start off with the loop index,
for(int i=1; i<a.length; i++)
if(a[i]>big)
big=a[i];
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;
}
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++)Here is a complete program which demonstrates this piece of code:
a[i]+=n;
import java.util.*;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.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();
}
}
import java.util.*;Note how introducing the method
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;
}
}
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];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
for(int i=0; i<a.length; i++)
b[i] = a[i]+n;
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.*;The version of
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;
}
}
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)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.
// 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;
}
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.*;As you can see, the method
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;
}
}
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.
Last modified: 14 December 2004