Structured Programming Revision and Java I/O

The first part of this set of notes is intended to revise topics that were covered in the Procedural Programming course and are assumed you are comfortable with in the Algorithms and Data Structures course. So please feel free to skip these notes unless you have found programming a bit difficult and want a rather gentler discussion than is contained in later notes. The second part of this set of notes says a little about the approach to input and output (I/O) taken in this course, which past experience suggests has confused some people.

In this course I have decided to have programs that read and write directly to the console window from which you issue Linux commands. I have also decided to use only code that is provided directly as part of the Java system. This means that some fairly complicated looking code has to be provided to enable you to read and write things. You needn't worry about this code, it isn't part of the course that you have to understand it, or be able to remember it and reproduce it under exam conditions. If you like, just forget it and take it on trust that it works as stated. But I give a little more explanation of it below for anyone who is interested.

The code presented in these revision notes can be found here.

Distinguishing the important code from the input/output code

Suppose we want to count the number of times a number occurs in a list of numbers. Note that we use the term "list" here just to mean an ordered sequence of numbers, and not in the specialist sense we use it in a later set of notes. Here is some code that will do what is required:
import java.util.*;
import java.io.*;

class CountNumber1
// A program to read a list of numbers then read one number and
// count the number of times it occurs in the list.
{
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.print("Enter a number: ");
line = in.readLine();
int n = Integer.parseInt(line);
int count=0;
for(int j=0; j<a.length; j++)
if(a[j]==n)
count++;
System.out.print("The number "+n);
System.out.println(" occurs "+count+" times in the list");
System.out.println("That's all");
}
}
Assuming this code is held in a file called CountNumber1.java, issuing the Linux command javac CountNumber1.java will cause a file called CountNumber1.class to be produced, and then issuing the Linux command java CountNumber1 will cause the program to be executed, issuing a prompt for a list of numbers to be typed in, a prompt for a further number to be typed in, and then printing the number of times this further number occurs in the list. For example, the following shows one possible run of the program:
Enter a list of numbers (all on one line):
21 67 8 19 21 3 64 52 21 12
Enter a number: 21
The number 21 occurs 3 times in the list
That's all

You will note immediately that some of the code doesn't look like the sort of things you have covered. That is because the code includes what is necessary to read the numbers from the screen, using only what is provided as a built-in part of the Java language. Up till now, in the Procedural Programming course you have had simplified input/output using a package provided by the author of the text book you used. The code here doesn't assume any pre-existing code except that provided as a standard part of Java. But, at this stage, don't worry about that. In fact the only code here that really does the main thing we are interested in: counting the number of times a number occurs in a list of numbers is the following lines:

  for(int j=0; j<a.length; j++)
if(a[j]==n)
count++;
With these lines, if the variable a refers to an array of integers, and the variable n holds a particular integer, after the code is executed, the variable count will hold an integer which is a count of the number of times the integer in variable n occurs in the array referenced by variable a.

Structured Programming

It is important that you think of code in terms of its structure, and not in terms of lines. So although the above code spreads over three lines, how you should really think of it is "an assignment inside an if statement inside a for loop". This could be expressed diagrammatically as:

This is why the indentation (putting spaces on the left-hand side) is important, since though you can't draw the boxes when writing Java code to show where one statement is inside another, you can show it by indentation. The line reading if(a[j]==n) starts a few spaces to the right of the line reading for(int j=0; j<a.length; j++) to show that it is inside the for-loop headed by that line. The line reading count++; starts a few more spaces to the right to show that it is inside the if-statement which is headed with if(a[j]==n). The line of code reading System.out.print("The number "+n); which comes after the important bit of code and just prints out a message giving the result is indented the same number of spaces as the line reading for(int j=0; j<a.length; j++), which shows that it is not part of the for-loop, it is a separate statement that comes after the for-loop. This could be shown diagrammatically as:

The indentation is purely to enable human viewers of the code to be able to make out its structure easily. Java treats any number of spaces and new lines equally as if it were a single space, so whether the code is indented or not does not alter how Java treats the code. So how does Java know which statement is inside which, for example that count++; is inside the if-statement which is inside the for-loop, while System.out.print("The number "+n); is not?

The answer is that a for-loop is defined as the header for(...;...;...) followed by a single statement, while an if-statement has two forms: either if(...) followed by a single statement, or if(...) followed by a single statement followed by else followed by another statement. So, following the for(...;...;...) header, only the next statement is in the for-loop. The only way multiple statements can be put into a for-loop is to use the brackets { and }. These are defined as turning a list of statements within the brackets into a single statement, so the following:

  {
statement1;
statement2;
.
.
.
statementn;
}
is regarded as a single statement, made up of n substatements.

This means that when we have:

  for(int j=0; j<a.length; j++)
if(a[j]==n)
count++;
System.out.print("The number "+n);
we can see that following for(int j=0; j<a.length; j++) there is no { so just the next statement is in the for-loop. The next statement starts with if(a[j]==n) so it must be an if-statement, but there is no { following that, so only the next statement is in the part of the if-statement obeyed if the test, a[j]==n evaluates to true. The next statement is count++; that is not followed by the word else so we know this is an if-statement without an else part. This means that the following statement System.out.print("The number "+n); is not part of the if-statement, and since only the if-statment is in the for-loop, is not part of the for loop either.

If we changed the code, putting in brackets so it read:

  for(int j=0; j<a.length; j++)
if(a[j]==n) {
count++;
System.out.print("The number "+n);
System.out.println(" occurs "+count+" times in the list"); }
System.out.println("That's all");
everything from the { to the } would form a single statement with three substatements. This means that running the code with the same number as above would cause
Enter a list of numbers (all on one line):
21 67 8 19 21 3 64 52 21 12
Enter a number: 21
The number 21 occurs 1 times in the list
The number 21 occurs 2 times in the list
The number 21 occurs 3 times in the list
That's all
to be printed. This is because the single statement which would be better written:
{
count++;
System.out.print("The number "+n);
System.out.println(" occurs "+count+" times in the list");
}
is inside the if-statement which is inside the for-loop. Diagrammatically, this could be shown as:

If that is the effect your really want, your code should be laid out to make the structure clear:

  for(int j=0; j<a.length; j++)
if(a[j]==n) {
count++;
System.out.print("The number "+n);
System.out.println(" occurs "+count+" times in the list");
}
System.out.println("That's all");
This is the style that was recommended to you previously. The closing bracket is directly below the start of the word which starts the statement. That's fine as a layout style, although my personal preference is to have the opening bracket on a line of its own below the header of the statement and indented, with its matching closing bracket directly below it, in this case:
  for(int j=0; j<a.length; j++)
if(a[j]==n)
{
count++;
System.out.print("The number "+n);
System.out.println(" occurs "+count+" times in the list");
}
System.out.println("That's all");
Either way makes the structure of the code clear by its layout. You will note that all recommendation for programming style in Java and similar languages say that individual lines of code should be consistently indented to show the depth within the structure. No-one recommends that closing brackets be put at the end of lines of code, where they would easily be missed. You should always think of the opening bracket and closing bracket as forming a pair with the code in between forming a block. If, when you are programming, you think in terms of moving or putting in or taking out individual {s and }s, it suggests you have the wrong approach to programming and need to change your way of thinking. Unfortunately, this wrong approach to programming can be encouraged by bad teaching, and badly-designed programming languages which you may have encountered beforehand outside university.

Java Input/Output

You were encouraged above to ignore the part of the code that reads
  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.print("Enter a number: ");
line = in.readLine();
int n = Integer.parseInt(line);
and just take it for granted that it reads from the screen in such a way that the array a is set to an array of a list of numbers typed in, and the variable n is set to a further number that is types in.

You may have noticed that whereas previously you wrote the code for a class, called say Fred with header

class Fred extends basic
here the code is written with header just
class CountNumber1
that is, without the extends basic. That was the bit that gave you access to the code written by the author of the textbook you used in your first programming course, which provided some simple input/output methods. We are not using that textbook in this course, though it is assumed you are familiar with the basic programing it covered. So I have decided not to carry on using the I/O methods from that textbook. Instead, I have decided to use I/O provided as part of the Java language, access to which is gained by the use of the import statements you see in the code example.

For a little more explanation, BufferedReader is a type of object defined in the Java package java.io. Because it is in this package, if you want to make use of it you have to put the line import java.io.*; in the file where you use it, above the class definition where it is used. An import statement like this is the only part of Java code that occurs outside a class definition.

The line

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
is actually a declaration of a variable called in of type BufferedReader. The variable could be called anything, it needn't be called in, although the variable that does what it does usually is. The declaration also contains an initialisation. The expression new BufferedReader(new InputStreamReader(System.in)) created a new object of type BufferedReader which reads from window the Linux command to run the Java program was issued from, and in is set to this. You needn't worry about why the expression has the form it has, if you wish to find out more, consult some Java reference.

The only thing you need to know about a BufferedReader object is that if buf is a variable referring to the object, then buf.readLine() is an expression which returns, as a Java String the next line of whatever the object is reading from, and sets it to read the line after that next time buf.readLine() is called. So, with in declared and initialised as above, in.readLine() will read the next line of text typed into the console window, and set the reader so the line after that is read if it is called again.

This means that the line

String line = in.readLine();
reads in the whole line of text making up the list of numbers. This line is broken up into individual numbers using something available in Java called a "string tokenizer". At this stage I am not going to give details on how it works, just take it that the lines of code
  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());
cause an array of integers of length equal to the number of integers in the line of code to be created, then puts the individual numbers into the array in order. The type StringTokenizer is given in the Java package java.util, which is why the import statement import java.io.*; was required. The code is not designed to handle anything but a list of integers separated only by spaces being typed in. If anything else is typed in when the program is executed, it will "crash", but that just means it will halt and print a lengthy error message, which you won't understand at this point.

It is worth discussing how to convert a string of characters representing an integer into the actual integer. The lines

  line = in.readLine();
int n = Integer.parseInt(line);
do this. The first reads a line of text from the screen which it is assumed are the characters which together represent a single integer. The variable called line is set to refer to it. Note, this is the same variable that was declared and set to a line of text representing a list of numbers in the statement
String line = in.readLine();
Now the variable is being reused to store something else, the line of characters typed in next to represent the integer being counted.

It is important to be aware of the fact that, for example, the string "365" is not at all the same thing as the integer 365. The first is the list of characters '3', '6' and '5'. The second is a number, which in the computer will be stored in a way that doesn't use 3, 6 or 5 at all, since that's just what is used in the decimal representation of the number. Computers, of course, store numbers in binary form. When programming in Java, if you have a string of characters which are the characters the decimal representation of an integer would take, you have to do something to get the actual integer.

The statement

int n = Integer.parseInt(line);
declares a new integer variable n and sets it to the value obtained by converting the string referenced by variable line into an integer. For any string referenced by variable str, the expression Integer.parseInt(str) will give the integer equivalent of the string. If str does not represent an integer, trying to convert it in this way will cause Java to "throw an exception". You needn't worry what "throwing an exception" means now, just assume for now it means the program will crash and some error message will be printed.

If this were a course on "how to use Java", we could go on in a lot more detail about Java input/output. We might move from what is here to discussing how Java is really meant for applications with a graphical user interface, not a plain text interface, and go into the details of graphics provided by Java. But this isn't that sort of course, it's a course on algorithms and data structures which just happens to use Java to give working programs because Java, for various reasons, has been adopted as the main language for teaching programming in Computer Science at Queen Mary. The important thing, however, is not Java, but the algorithms and data structures. We will only cover enough Java to support the programs you have to run, write and understand to learn about the algorithms and data structures this course is really about. Even then, most of the code to do with input/output you can just take on trust and don't need to think about too much. Obviously, if the intricacies of Java do interest you, you could study more about them in your own time out of interest. A good place to start would be the official Java tutorial. You might also find some useful links on a page I maintain, Useful and interesting links for new Java programmers.


Matthew Huntbach

Last modified: 13 December 2002