Section 3.3
The while and do..while Statements
STATEMENTS IN JAVA CAN BE either simple
statements or compound statements. Simple statements, such
as assignments statements and subroutine call statements, are
the basic building blocks of a program. Compound statements, such
as while loops and if statements, are used to
organize simple statements into complex structures, which are called
control structures because they control the order in which the statements
are executed. The next four sections explore the details of
all the control structures that are available in Java, starting
with the while statement and the do..while
statement in this section. At the same time, we'll look at examples
of programming with each control structure and apply the techniques
for designing algorithms that were introduced in the
previous section.
The while Statement
The while statement was already introduced in Section 1.
A while loop has the form
while ( boolean-expression )
statement
The statement can, of course, be a block
statement consisting of several statements grouped together between a pair of braces.
This statement is called the body of the loop. The body
of the loop is repeated as long as the boolean-expression
is true. This boolean expression is called the continuation condition,
or more simply the test, of the loop.
There are a few points that might need some clarification. What happens if
the condition is false in the first place, before the body of the loop is executed
even once? In that case, the body of the loop is never executed at all. The body of
a while loop can be executed any number of times, including zero. What happens if
the condition is true, but it becomes false somewhere in the middle
of the loop body? Does the loop end as soon as this happens? It does not, because
the computer continues executing the body of the loop until it gets to the end.
Only then does it jump back to the beginning of the loop and test the condition,
and only then can the loop end.
Let's look at a typical problem that can be solved using a while loop:
finding the average of a set of positive integers entered by the user. The average
is the sum of the integers, divided by the number of integers. The program will
ask the user to enter one integer at a time. It will keep count of the number of
integers entered, and it will keep a running total of all the numbers it has
read so far. Here is a pseudocode algorithm for the program:
Let sum = 0
Let count = 0
while there are more integers to process:
Read an integer
Add it to the sum
Count it
Divide sum by count to get the average
Print out the average
But how can we test whether there are more integers to process? A typical solution
is to tell the user to type in zero after all the data have been entered. This will work
because we are assuming that all the data are positive numbers, so zero is not a
legal data value. The zero is not itself part of the data to be averaged. It's just
there to mark the end of the real data. A data value used in this way is sometimes
called a sentinel value. So now the test in the
while loop becomes "while the input integer is not zero". But there is
another problem! The first time the test is evaluated, before the body of the
loop has ever been executed, no integer has yet been read. There is no "input
integer" yet, so testing whether the input integer is zero doesn't make sense.
So, we have to do something before the while loop to make sure
that the test makes sense. Setting things up so that the test in a while
loop makes sense the first time it is executed is called priming
the loop. In this case, we can simply read the first integer before
the beginning of the loop. Here is a revised algorithm:
Let sum = 0
Let count = 0
Read an integer
while the integer is not zero:
Add the integer to the sum
Count it
Read an integer
Divide sum by count to get the average
Print out the average
Notice that I've rearranged the body of the loop. Since an integer is read
before the loop, the loop has to begin by processing that integer. At the end of
the loop, the computer reads a new integer. The computer then jumps back to
the beginning of the loop and tests the integer that it has just read. Note that
when the computer finally reads the sentinel value, the loop ends before the
sentinel value is processed. It is not added to the sum, and it is not counted.
This is the way it's supposed to work. The sentinel is not part of the data.
The original algorithm, even if it could have been made to work without priming,
was incorrect since it would have summed and counted all the integers, including
the sentinel. (Since the sentinel is zero, the sum would still be correct,
but the count would be off by one. Such so-called off-by-one errors
are very common. Counting turns out to be harder than it looks!)
We can easily turn the algorithm into a complete program. Note that the program
cannot use the statement "average = sum/count;" to compute
the average. Since sum and count are both variables of
type int, the value of sum/count is an integer. The average
should be a real number. We've seen this problem before: we have to convert
one of the int values to a double to force the computer
to compute the quotient as a real number. This can be done by type-casting
one of the variables to type double. The type cast "(double)sum"
converts the value of sum to a real number, so in the program the
average is computed as "average = ((double)sum) / count;".
Another solution in this case would have been to declare sum to be
a variable of type double in the first place.
One other issue is addressed by the program: If the user enters zero as the
first input value, there are no data to process. We can test for this case by
checking whether count is still equal to zero after the while
loop. This might seem like a minor point, but a careful programmer should
cover all the bases.
Here is the program and an applet that simulates it:
public class ComputeAverage {
/* This program reads a sequence of positive integers input
by the user, and it will print out the average of those
integers. The user is prompted to enter one integer at a
time. The user must enter a 0 to mark the end of the
data. (The zero is not counted as part of the data to
be averaged.) The program does not check whether the
user's input is positive, so it will actually work for
both positive and negative input values.
*/
public static void main(String[] args) {
int inputNumber; // One of the integers input by the user.
int sum; // The sum of the positive integers.
int count; // The number of positive integers.
double average; // The average of the positive integers.
/* Initialize the summation and counting variables. */
sum = 0;
count = 0;
/* Read and process the user's input. */
TextIO.put("Enter your first positive integer: ");
inputNumber = TextIO.getlnInt();
while (inputNumber != 0) {
sum += inputNumber; // Add inputNumber to running sum.
count++; // Count the input by adding 1 to count.
TextIO.put("Enter your next positive integer, or 0 to end: ");
inputNumber = TextIO.getlnInt();
}
/* Display the result. */
if (count == 0) {
TextIO.putln("You didn't enter any data!");
}
else {
average = ((double)sum) / count;
TextIO.putln();
TextIO.putln("You entered " + count + " positive integers.");
TextIO.putln("Their average is " + average + ".");
}
} // end main()
} // end class ComputeAverage
The do..while Statement
Sometimes it is more convenient to test the continuation condition at the end
of a loop, instead of at the beginning, as is done in the while loop.
The do..while statement is very similar to the while statement,
except that the word "while," along with the condition that it
tests, has been moved to the end. The word "do" is added to mark
the beginning of the loop. A do..while statement has the form
do
statement
while ( boolean-expression );
or, since, as usual, the statement
can be a block,
do {
statements
} while ( boolean-expression );
Note the semicolon, ';', at the end. This semicolon is part of the
statement, just as the semicolon at the end of an assignment statement
or declaration is part of the statement. Omitting it is a syntax error.
(More generally, every statement in Java ends either with a
semicolon or a right brace, '}'.)
To execute a do loop, the computer first executes the body
of the loop -- that is, the statement or statements inside the loop --
and then it evaluates the boolean expression. If the value of the
expression is true, the computer returns to the beginning
of the do loop and repeats the process; if the value is false,
it ends the loop and continues with the next part of the program.
Since the condition is not tested until the end of the loop,
the body of a do loop is executed at least once.
For example, consider the following pseudocode for a
game-playing program. The do loop makes sense here
instead of a while loop because with the do loop,
you know there will be at least one
game. Also, the test that is used at the end of the loop
wouldn't even make sense at the beginning:
do {
Play a Game
Ask user if he wants to play another game
Read the user's response
} while ( the user's response is yes );
Let's convert this into proper Java code. Since I don't want to talk about
game playing at the moment, let's say that we have a class named Checkers,
and that the Checkers class contains a static member subroutine named
playGame() that plays one game of checkers against the user.
Then, the pseudocode "Play a game" can be expressed as the subroutine
call statement "Checkers.playGame();". We need a variable
to store the user's response. The TextIO class makes it convenient
to use a boolean variable to store the answer to a yes/no question.
The input function TextIO.getlnBoolean() allows the user to enter
the value as "yes" or "no". "Yes" is considered to
be true, and "no" is considered to be false. So,
the algorithm can be coded as
boolean wantsToContinue; // True if user wants to play again.
do {
Checkers.playGame();
TextIO.put("Do you want to play again? ");
wantsToContinue = TextIO.getlnBoolean();
} while (wantsToContinue == true);
When the value of the boolean variable is set to true,
it is a signal that the loop should end. When a boolean variable
is used in this way -- as a signal that is set in one part of the program
and tested in another part -- it is sometimes called a flag
or flag variable (in the sense of a signal flag).
By the way, a more-than-usually-pedantic programmer would sneer at the
test "while (wantsToContinue == true)". This test is
exactly equivalent to "while (wantsToContinue)".
Testing whether
"wantsToContinue == true" is true amounts to the
same thing as testing whether "wantsToContinue" is true.
A little less offensive is an expression of the form "flag == false",
where flag is a boolean variable. The value of "flag == false"
is exactly the same as the value of "!flag", where ! is the boolean
negation operator. So you can write
"while (!flag)" instead of "while (flag == false)",
and you can write "if (!flag)" instead of
"if (flag == false)".
Although a do..while statement is sometimes more convenient than
a while statement, having two kinds of loops does not make the
language more powerful. Any problem that can be solved using do..while
loops can also be solved using only while statements, and vice versa.
In fact, if doSomething represents
any block of program code, then
do {
doSomething
} while ( boolean-expression );
has exactly the same effect as
doSomething
while ( boolean-expression ) {
doSomething
}
Similarly,
while ( boolean-expression ) {
doSomething
}
can be replaced by
if ( boolean-expression ) {
do {
doSomething
} while ( boolean-expression );
}
without changing the meaning of the program in any way.
The break and continue Statements
The syntax of the while and do..while loops
allows you to test the continuation condition at either the beginning of a loop or at the end.
Sometimes, it is more natural to have the test in the middle
of the loop, or to have several tests at different places
in the same loop. Java provides a general method for
breaking out of the middle of any loop. It's called the
break statement, which takes the form
break;
When the computer executes a break statement in a loop, it will immediately
jump out of the loop. It then continues on to whatever follows
the loop in the program. Consider for example:
while (true) { // looks like it will run forever!
TextIO.put("Enter a positive number: ");
N = TextIO.getlnlnt();
if (N > 0) // input is OK; jump out of loop
break;
TextIO.putln("Your answer must be > 0.");
}
// continue here after break
If the number entered by the user is greater than zero, the break
statement will be executed and the computer will jump out of the loop.
Otherwise, the computer will print out "Your answer must be > 0."
and will jump back to the start of the loop to read another input value.
(The first line of the loop, "while (true)" might look
a bit strange, but it's perfectly legitimate. The condition in a while
loop can be any boolean-valued expression. The computer evaluates this
expression and checks whether the value is true or false.
The boolean literal "true" is just a boolean expression
that always evaluates to true. So "while (true)" can
be used to write an infinite loop, or one that can be terminated only
by a break statement.)
A break statement terminates the loop that immediately encloses
the break statement. It is possible to have nested
loops, where one loop statement is contained inside another.
If you use a break statement inside a nested loop, it will only
break out of that loop, not out of the loop that contains the
nested loop. There is something called a "labeled break"
statement that allows you to specify which loop you want to break.
I won't give the details here; you can look them up if you
ever need them.
The continue statement is related to break,
but less commonly used. A continue statement tells the
computer to skip the rest of the current iteration of the loop.
However, instead of jumping out of the loop altogether, it
jumps back to the beginning of the loop and continues with the
next iteration (after evaluating the loop's continuation
condition to see whether any further iterations are required).
break and continue can be used in while
loops and do..while loops. They can also be used in for loops,
which are covered in the next section. In Section 6,
we'll see that break can also be used to break out of a switch statement.
Note that when a break occurs inside an
if statement, it breaks out of the loop or switch statement that
contains the if statement. If the if statement is not contained
inside a loop or switch, then the if statement cannot legally contain
a break statement. A similar consideration applies to continue statements.