Loops in Java
The process of repeatedly doing the same thing over and over again is called a loop. In the previous section, we the most basic data structure, an array. Loops are a common way to work with data stored in array. There are two types of loops — while-loops and for-loops.
The while-Loop.
The simplest loop is the while-loop. It takes the following form in Java:
while (<condition>) {
<statements>
}
Where is an expression evaluating to true or false, and are the statements to execute.
In the while-loop, we pass into the parentheses some code that outputs true
or false. Most often, these are Boolean expressions. As long as the
condition, in the example above x
, returns true
, the code block
enclosed by the while-loop executes.
In sum, the while-loop has two parts:
-
A condition that must be true each time the loop is entered, called the test statement (represented by
x
in the syntax above); and -
a block of code to repeat (represented by the set of ellipses above)
As soon as the condition is false, the block of code is not executed, and execution continues after the loop. Otherwise, the block of code continues executing. In loops, each execution of a loop's block of code is called an iteration. For example:
int iteration = 0;
while (iteration < 4) {
System.out.println(iteration);
iteration++;
}
System.out.println("Done");
0
1
2
3
Done
The operator ++
is the increment-by-1 operator; it adds the integer 1 to
a particular value. This will cause different inputs for the while-loop,
allowing it evaluate to either true
or false
. We call this the
updating statement. In the code above, the condition is iteration < 4
.
Since iteration = 0
, the condition in plain English is, “As long as
iteration
is less than 4
, execute the following.” The first time
we encounter the while-loop, iteration = 0
; the condition returns true
.
So, we print the current value assigned to increment
, 0
, and then we
increment: iteration = 1
. We go back to the condition: Is 1 < 4
? Yes.
Print 1
and increment: iteration = 2
. Is 2 < 4
? Yes. Print 2
and
increment: iteration = 3
. Is 3 < 4
? Yes. Print 3
and increment:
increment = 4
. Is 4 < 4
? No. Java ignores the while-loop's code block
and moves to the next statement — print "Done"
.
The do-while Loop.
The do-while
loop takes the following form:
do {
⟨_expressions_⟩
} while ( ⟨_condition_⟩ )
Compared to the while-loop, the do-while
loop checks the condition
after the expressions are executed. In effect, the do while
loop
operates similarly to the while-loop (it is still a loop), with the
difference being the loop's body always executes at least once (in contrast
to while-loops, where no such requirement exists; the loop body may never
execute if the first condition check returns false
).
// the loops below have the same condition
// only one will have its body executed
int i = 0;
while (i < 0) {
System.out.println("while " + i);
i++
};
i = 0;
do {
System.out.println("do-while" + i);
i++;
} while (i < 0)
do-while 0
Infinite Loops
If we're going to use a while-loop, we must have code that eventually
causes its condition to return false
. In other words, we need to ensure
that the while-loop will eventually halt. Otherwise, the loop becomes an
unterminated loop, which will cause our code to hang, or hit a
timeout.
Most often, such code is included in the while-loop's body. If we fail to write this code, the while-loop's code block will continue executing endlessly — an infinite loop. Here is an example of an unterminated while-loop:
int index = 0;
while (index < 4) {
System.out.println(index);
index--;
}
System.out.println("Done");
The while-loop never terminates because its condition will always return
true
. The while-loop here will run for as long as it can until the
console eventually returns a (Program Timed Out)
. Here is a more explicit
example of an unterminated loop:
while (true) {
System.out.println("Looping");
}
This is such a clear example of an unterminated loop that we have a special
name for it — the while true
loop. If we ever feel the need to use
a while true
loop, we must always ask ourselves what we're using it for
— we're playing with fire.
while-loops and Arrays
With the while-loop, we can loop through the values in an array:
char[] word = {'j', 'a', 'v', 'a'};
int index = 0;
while (index < 4) {
System.out.println(word[index]);
index++;
}
System.out.println("Done");
j
a
v
a
Done
The for-loop
Looping through data structures like arrays is so common in programming that we have an alternative and cleaner way of looping, the for-loop. The syntax is the following:
for (int i = 0; i < n; i++) {
...
...
...
}
/*
where:
i is an index (an integer)
n is the number of times we want to loop
... is a statement to execute
*/
The for-loop accomplishes the same task as the while-loop — it's just cleaner. Compare the two:
int i = 0;
while (i < n) {
...
...
...
}
for (int i = 0; i < n; i++) {
...
...
...
}
Notice that in the for-loop, there are four parts:
-
int i = 0;
is the counter, a variable to be used by the test condition. This variable is only available inside the loop. We cannot access it outside the loop. -
i < 4;
is the condition to test, called the test statement. This is checked every time we enter the loop, including the first time. As long as this condition is true, the block of code is executed, and the loop continues. As soon as the condition is false, the loop terminates, and Java ignores all statements thereafter. -
i++
is the code that changes the index, causing different inputs to the test's condition, called the updating statement. This code is executed only when we return to the top of the loop. It is not executed the first time. -
{ ... }
contain the statements to execute.
The first three parts (the statements contained inside the parentheses), is called the for-loop's header, and the block of code to execute is called the for-loop's body. Bearing these parts in mind, an example:
char[] word = {'j', 'a', 'v', 'a'};
for (int index = 0; index < 4; index++) {
System.out.println(word[index]);
}
System.out.println("Done");
j
a
v
a
Done
Again, a for-loop operates exactly the same as a while-loop, just shorter and more concise. Here is a while-loop implementation:
int[] primes = {2, 3, 5, 7, 11};
int i = 0;
while (i < 5) {
System.out.println(primes[i]);
i++;
}
System.out.println("Done");
2
3
5
7
11
Done
And here is a for-loop implementation:
int[] primes = {2, 3, 5, 7, 11};
for (int i = 0; i < primes.length; i++) {
System.out.println(primes[i]);
}
System.out.println("Done");
2
3
5
7
11
Done
A helpful way to loop through the array is to use the array's .length
property. This spares us from manually counting each of the individual
elements in the array; a task that may be impossible or cumbersome with
large arrays.
For-loop Nuances.
In a for-loop, the counter variable is initialized only once. It does not get “reinitialized” at each iteration. The value assigned to it changes, but there is no new initialization. The loop's test expression is evaluated every time the loop block is executed, including the first time. The loop's updating statement is executed after each time the block is executed and before the condition is checked. Putting altogether, the algorithm consists of the following steps in order:
- Evaluate the condition.
- If the condition is false, ignore the for-loop's block of code and go to the statement after the for-loop.
- If the condition is true, execute the loop's block of code. After the block finishes executing, update the loop variable.
- Repeat.
Changes to a Loop Variable
We can change a variable initialized outside a while-loop with code inside the while-loop:
int i = 0;
while (i < 10) {
i++;
}
System.out.println(i);
10
The same goes for a for-loop:
int num = 0;
for (int i = 0; i < 10; i++) {
num++;
}
System.out.println(num);
10
Incomplete for-loops
We are not required to provide the counter, test statement, or the update statement inside the for-loop's header. They can be stated outside the for-loop, and used inside the header, or omitted entirely:
int i = 0;
for (; i < 10; i++) {
// this will execute 10 times
}
for (; i < 10; ) {
// this will execute exactly once
}
for (;;) {
// this will hang
}
Note, we should not write loops this way unless we have a good reason for doing so — such code is difficult to read and a breeding ground for bugs.
Loop Control.
We can control loops by using the keywords break
and continue
. The
keyword break
tells Java “Get out of this loop now” We might
use this if our loop eventually returns the data we sought, or if a
particular condition occurs. For example, we might search a particular
array, and once a match is found, we break
, rather than continuing to
check the remaining values. For example:
Oppositely, the word continue
tells Java, “Get back to the top of
the loop now, perform the update, and continue to the code block.”
This keyword is often used when our loop runs, and returns data that isn't
interesting. For example, we might have an array of user emails, and we
only want to send a newsletter to those who agreed to receiving
newsletters. We loop through all of the user emails — if the user
opted to receive newsletters, we send the newsletter, otherwise, we
continue
.
Here is an example utilizing the two key words:
int query = 5;
int[] primes = {2, 3, 5, 7};
for (int i = 0; i < primes.length; i++) {
if (primes[i] == query) {
System.out.println("Match found: " + query);
break;
} else if ((primes[i] != query) && (i < primes.length-1)) {
continue;
} else {
System.out.println("No match: " + query);
break;
}
}
System.out.println("Loop exited");
Match found: 5
Loop exited
Enhanced for-loop
Going through the values in an array one at a time is so common in programming that Java provides syntactic sugar for it. On the left is a regular for-loop, and on the right is the enhanced for-loop:
// Regular for loop
int[] arr = {0, 1, 2, 3};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// Enhanced for loop
int[] arr = {0, 1, 2, 3};
for (int element : arr) {
System.out.println(element);
}
If we just want to iterate through the values inside an array, we can use the enhanced for-loop. It's cleaner, more concise, and we don't need to worry about indexes. The tradeoff: We don't have access to a indices. Thus, if our computation needs access to index, we should use the regular for-loop.
While- v. For-loops
If we want to execute a statement or set of statements as long as some condition is true, but we do not know how many times the statement should execute, a while-loop would be best to use. while-loops are an example of an indefinite loop. If, however, we know exactly how many times we want to iterate or we have data that provides such number, a for-loop is ideal — it is a definite loop.
Fencepost Problem
A very common error when handling for-loops is the fencepost problem (also called of-by-one error). Suppose we wanted to build a fence with 5 posts and 4 wires between the posts. The fencepost problem is represented with the following diagram:
In its simplest form, the problem occurs because a procedure involves two steps, say and and must be repeated times, while must be repeated times. Whenever we see this procedural pattern, we must first execute before we begin iterating and together, as seen in the diagram above.
Let's consider some exercises. How many times do the following loops execute?
int counter = 0;
for (int loop = 4; loop <= 8; loop += 2) {
counter++;
}
System.out.println(counter);
Here, the counter starts at 0. The loop variable starts at 4. As long as it
is less than or equal to 8, we execute, and update the counter. After each
execution, the counter updates by 2. Thus, at loop = 4
, the test
statement returns true
. So, counter = 1
, and loop = 6
. Is 6 <= 8
?
Yes. counter = 2
; loop = 8
. Is 8 <= 8
? Yes. counter = 3
;
loop = 10
. Is 10 <= 8
? No. We leave the loop and go to the next
statement, print counter
. Counter was updated to 3
, so this is the
value printed out:
3
How about this one:
int count = 0;
for (int i = 10; i >= 10; i -= 3) {
count++;
}
System.out.println(count);
The variable count
starts at 0, and it is updated by 1 inside the loop.
The loop's counter starts at i = 10
. If i >= 10
, we execute then
decrement i
by 3. Thus, the first time: i = 10
. Condition is true, so
count = 1
, and i = 7
. Test, and the condition is false —
Thus, the loop executes exactly once: count = 1
.
Now this one:
int i = 0;
for (int j = 2; j >= 4; j++) {
i++;
}
System.out.println(i);
This loop never executes. Remember, the test statement is evaluated the
first time. In this case, the test statement returns false
—
0
Next, given the array below, print each element with a loop:
char[] letters = {'a', 'b', 'c', 'd'};
Using a for-loop:
char[] letters = {'a', 'b', 'c', 'd'};
for (int i = 0; i < letters.length; i++) {
System.out.println(letters[i]);
}
a
b
c
d
Or, with an enhanced for-loop:
char[] letters = {'a', 'b', 'c', 'd'};
for (char elements : letters) {
System.out.println(elements);
}
a
b
c
d
Given the array below, print every other element on the same line:
char[] letters = {'b', 'h', 'y', 'e', 'e', 'y'};
Here is one implementation:
char[] letters = {'b', 'h', 'y', 'e', 'e', 'y'};
for (int i = 1; i <= letters.length; i+=2) {
System.out.print(letters[i]);
}
hey
Given the array below, print only the elements with even indices:
int[] nums = {1, 2, 3, 4, 5, 6, 7, 8};
Here's one solution:
int[] nums = {1, 2, 3, 4, 5, 6, 7, 8};
for (int i = 2; i < nums.length; i+=2) {
System.out.println(nums[i]);
}
3
5
7
Given the array below, print the sum of all its elements:
int[] primesArr = {2, 3, 5, 7, 11, 13, 17, 19, 23};
Here is one possible implementation:
int[] primesArr = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int primesArrSum = 0;
for (int i = 0; i < primesArr.length; i++) {
primesArrSum += primesArr[i];
}
System.out.println(primesArrSum);
100