Loops revision - interrupted 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 we covered loops which went through a whole array, one element at a time. For each element some operation was performed. A general pattern for this was:

   for(int i=0; i<a.length; j++)
dosomething
The dosomething here could be any Java statement, including several statements made into one by starting with { and finishing with }.

A method which returns a boolean

Suppose we wanted to find whether a particular integer occurred in an array of integers. We could do this by having some boolean variable which is initialised to false. Then we could loop through the array using the pattern above, and at any point where a[i] is equal to the integer, set the boolean variable to true. So our dosomething, supposing the boolean variable is called found and the integer we are looking for is stored in a variable called n, is:
   if(a[i]==n)
found=true;
Here is this code put into a static method, with the initialisation of found:
  public static boolean member(int n,int[] a)
{
boolean found=false;
for(int i=0; i<a.length; i++)
if(a[i]==n)
found=true;
return found;
}
In order that you can test this method, here is a simple program that makes use of it:
import java.util.*;
import java.io.*;

class Member1
// A program to read a list of numbers then read one number and
// say whether it is in the list
{
public static void main(String[] args) throws IOException
{
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.print("Enter a number: ");
int n = readANumber(in);
if(member(n,a))
System.out.println("The number is in the list");
else
System.out.println("The number is not in the list");
}

public static int[] readNumbers(BufferedReader in) throws IOException
{
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 IOException
{
String line = in.readLine();
int n = Integer.parseInt(line);
return n;
}

public static boolean member(int n,int[] a)
{
boolean found=false;
for(int i=0; i<a.length; i++)
if(a[i]==n)
found=true;
return found;
}
}
Please remember, as usual, the important thing here is not the whole program, but the method which demonstrates the loop pattern we are interested in. You might like to note, however, that as this method returns a boolean value we can put a call to it directly in the test part of an if-statement. The if-statement above that is written:
  if(member(n,a))
System.out.println("The number is in the list");
else
System.out.println("The number is not in the list");
could be written with a separate variable to hold the result of the method call:
  boolean flag=member(n,a);
if(flag)
System.out.println("The number is in the list");
else
System.out.println("The number is not in the list");
but there is no need for this, unless for some reason we wanted to save the value of the call member(n,a) for use at a later stage. Declaring a variable whose only purpose is to hold the result of a method call which is then immediately used in the next statement doesn't make much sense. You might as well put the method call in the place where you would put the variable, it means the same thing but makes the code easier to understand because there is less detail to follow. The one time when you might want to declare a separate variable like this (apart from cases where you need to use the result of the method call somewhere else as well) is when you have a very complicated expression, maybe a method call as an argument to a method call, which itself is an argument to a method call, ..., and so on. In this case the use of variables declared just to temporarily hold values may help break up a complicated expression into smaller parts which makes it easier to understand.

Interrupted loops

The method we gave above for member goes through the whole array, but there is no need for it to do so. Once it has found the number it is searching for, there is no need for it to go further in the array, it can immediately return true. One way we could do this is to use the Java break statement, making the member method:
 public static boolean member(int n,int[] a)
{
boolean found=false;
for(int i=0; i<a.length; i++)
if(a[i]==n)
{
found=true;
break;
}
return found;
}
The effect of executing a break statement is that execution of the loop it is in is immediately halted, and the statement following the loop is executed next. In the above example, this means the loop is exited when a[i] has the value equal to n, even though the continuation condition in the loop header, i<a.length remains true. The break statement here is inside the if-statement, which is inside the loop. In fact, it only makes sense for a break statement to be inside an if-statement inside a loop, since if it wasn't inside an if-statement, it would always be executed, and the loop would always be exited on the first time round it. The structure of the code above could be shown diagrammatically as:

We call this an "interrupted loop" because the loop may be exited before the conditions indicated in its header for exiting are reached (in this case, i<a.length becoming false, i.e. we have gone through the whole array).

Interrupting a loop with a break is sometimes a good way to express a problem, but it's a technique that should be used with caution. In particular, it should not be used for loops with large amounts of code inside the loop, since then it might be easy to miss the fact that there's a break inside and the loop could exit in conditions other than those indicated by the loop header.

Another way of dealing with this would be to indicate in the header that we are leaving the loop either when found becomes equal to true or when i becomes equal to the length of the array. The condition given in the header of a for-loop, however, is the condition for continuing execution of the loop, which is the negation of the condition for exiting it. We continue the loop if found is not equal to true and i is less than the length of the array. We could write this as found!=true&i<a.length. But since found is itself a boolean value, the expression found!=true evaluates to true only when found is false, and an easier way of expressing the same thing is !found. Remember that ! is the way to write the logic operator "not" in Java. This gives us the following code for the method:

 public static boolean member(int n,int[] a)
{
boolean found=false;
for(int i=0; !found&i<a.length;!found; i++)
if(a[i]==n)
found=true;
return found;
}
We could note here that since found becomes true when a[i] is equal to n and remains false before that, we could test the value of a[i] against n directly in the loop header. This would give us:
  for(int i=0; a[i]!=n&i<a.length; i++)
but then what would go into the body of the for-loop? The answer is "nothing". It is possible to have a for-loop which consists of just the header - or almost just the header. All that is required to complete it is a semi-colon at the end, in this case:
  for(int i=0; a[i]!=n&i<a.length; i++);
What this means is that the statement inside the for-loop is the "empty statement" in other words, the statement that does nothing and is written as nothing, but such a statement still has to have the semi-colon to end it. Diagrammatically, this could be shown as:

Another way, which is perhaps a little clearer, to write the empty statement is {}.

There are two problems with loop proposed above. The first is how can we tell, when it terminates, whether we have found the integer we are looking for or not. The answer is that the final value of i tells us, since if it is less than a.length, the loop has terminated because a[i]!=n is no longer true, that is we have found a value of i such that a[i] is equal to n. If the final value of i is a.length then i has run through all the values from 0 to the length of the array and not found a case where a[i] is equal to n, so we can conclude that n does not occur in the array.

The second problem is that when we evaluate a[i]!=n&i<a.length in the loop, we always evaluate both a[i]!=n and i<a.length. But if we have gone through all the array and i has reached the value a.length, there is no array cell a[i], so attempting to evaluate a[i]!=n will result in the program crashing (or, to be technical, "throwing an IndexOutOfBoundsException"). To avoid this, we can make use of Java's "short circuit" and operation, written &&. This is defined as exp1&&exp2, where exp1 and exp2 are any expressions that evaluate to boolean values, evaluating first exp1. If exp1 evaluates to false, the whole expression evaluates to false without exp2 ever being attempted. If exp1 evaluates to true, then exp2 is evaluated and its result given as the result of the whole expression exp1&&exp2. Therefore, if we have i<a.length&&a[i]!=n, first i<a.length is evaluated, and if it evaluates to false, i.e. i is equal to or greater than a.length, then a[i]!=n is not evaluated at all, so it does not matter that i has a value which would cause any attempt to find the value of a[i] to cause the program to crach.

So we could write the loop:

for(int i=0; i<a.length&&a[i]!=n; i++) {}
but that still causes us problems. A for-loop lets us declare a variable in the initialisation part of the loop, as in int i=0 above. We have used this as the standard pattern for a loop that goes through an array. But this means the variable declared is local to the loop, we cannot access it outside the loop. That means we cannot tell whether i has a value less than a.length once the loop has finished. In order to be able to do this, we have to declare i outside the loop. Putting this together gives us the following revised code for the static method member:
 public static boolean member(int n,int[] a)
{
int i;
for(i=0; i<a.length&&a[i]!=n; i++) {}
return i<a.length;
}
The {} indicates that the loop has only the empty statement within it, so the following statement is not in the loop. The whole code for the method could be shown diagrammatically as:

You might be surprised at the last statement, and wonder why it isn't:

  if(i<a.length)
return true;
else
return false;
The answer is that i<a.length is a boolean expression which evaluates to true or false, so we can just return its value. Whenever you're writing a method that returns a boolean, if you find yourself ending it with:
  if(test)
return true;
else
return false;
for any expression test it's always neater to write instead:
  return test;

An alternative approach to this problem would be to note that executing a return statement anywhere in a method causes the method to be halted and the value given in the return statement to be returned as the result of the method. So we could write the method as:

 public static boolean member(int n,int[] a)
{
for(int i=0; i<a.length; i++)
if(a[i]==n)
return true;
return false;
}
So here, if a[i] is equal to n the statement return true is executed, and even though it is in the middle of the loop, the whole method terminates. Note the structure of the code here could be shown as:

The statement return false; is outside the for-loop, its in only executed and false returned by the method if the loop goes through the whole array and terminates with i equal to a.length. Note, do not confuse this with:

  // Deliberately silly code
for(int i=0; i<a.length; i++)
if(a[i]==n)
return true;
else
return false;
which, for some reason, novice programmers often seem to write. In this case, each time round the loop, whenever a[i] equals n the method terminates and returns true, and whenever a[i] is not equal to n the method terminates and returns false. In other words, the method terminates and returns either true or false on every time round it, so it will only be gone round once, returning true if the first element in the array is equal to n and returning false if the first element in the array is not equal to n. This can be shown diagrammatically as:

It is clearly not the desired behaviour, nor, since it only ever goes round the loop once or zero times, a piece of code that is ever sensible.


Matthew Huntbach

Last modified: 14 December 2004