Skip to content
elephantoo

Loops: for, while & do-while

Lesson 6 of 43 15 min read

Counted and conditional loops, the enhanced for, break, continue, labels and nested loops.


Computers are brilliant at doing the same thing many times without getting bored. A loop repeats a block of code: printing every item in a list, retrying until the user enters a valid password, adding up a million numbers. Java has four kinds of loop, plus break and continue to control them.

while: repeat while a condition is true#

Countdown.java
public class Countdown {
    public static void main(String[] args) {
        int n = 5;
        while (n > 0) {
            System.out.print(n + " ");
            n--;
        }
        System.out.println("Liftoff!");
    }
}
Output
5 4 3 2 1 Liftoff!

The condition is checked before each pass. If it is false at the start, the body never runs. Something inside the loop must eventually make the condition false (here n--), or you get an infinite loop. Press Ctrl+C in the terminal to stop a runaway program.

while is ideal when you do not know in advance how many repetitions you need:

Doubling.java
public class Doubling {
    public static void main(String[] args) {
        double savings = 1000;
        int years = 0;
        while (savings < 2000) {
            savings *= 1.07;   // 7% growth per year
            years++;
        }
        System.out.println("Doubled after " + years + " years");
    }
}
Output
Doubled after 11 years

do-while: run at least once#

A do-while checks the condition after the body, so the body always runs at least once. It is perfect for menus and "ask until valid" input:

Java
Scanner in = new Scanner(System.in);
int choice;
do {
    System.out.println("1) Play  2) Scores  3) Quit");
    choice = in.nextInt();
} while (choice != 3);

Note the semicolon after while (...) at the end.

for: counted loops#

When you know how many times to repeat, the for loop keeps the counter logic in one place:

Java
for (int i = 0; i < 5; i++) {
    System.out.print(i + " ");   // 0 1 2 3 4
}

The header has three parts separated by semicolons:

  1. Initialisation int i = 0: runs once before the loop.
  2. Condition i < 5: checked before every pass; the loop stops when it is false.
  3. Update i++: runs after every pass.

The counter i exists only inside the loop. Any step works:

ForVariations.java
public class ForVariations {
    public static void main(String[] args) {
        for (int i = 10; i >= 0; i -= 2) {   // count down in twos
            System.out.print(i + " ");
        }
        System.out.println();

        int sum = 0;
        for (int i = 1; i <= 100; i++) {     // add 1..100
            sum += i;
        }
        System.out.println("Sum 1..100 = " + sum);

        for (int i = 1; i <= 5; i++) {       // a times table
            System.out.println("7 x " + i + " = " + (7 * i));
        }
    }
}
Output
10 8 6 4 2 0
Sum 1..100 = 5050
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35

Off-by-one errors are the most common loop bug. i < 5 runs 5 times starting from 0; i <= 5 runs 6 times. When looping over an array of length n, use i < n because indexes go from 0 to n - 1.

The enhanced for (for-each)#

To visit every element of an array or collection, the enhanced for is shorter and cannot go out of bounds:

ForEach.java
public class ForEach {
    public static void main(String[] args) {
        String[] languages = {"Java", "Python", "Go"};
        for (String lang : languages) {
            System.out.println("I like " + lang);
        }

        int[] prices = {120, 80, 250};
        int total = 0;
        for (int p : prices) {
            total += p;
        }
        System.out.println("Total: " + total);
    }
}
Output
I like Java
I like Python
I like Go
Total: 450

Read for (String lang : languages) as "for each lang in languages". The trade-off is that you do not get the index, and assigning to the loop variable does not change the array. Use a classic for when you need the index or want to modify elements.

break: leave the loop early#

break jumps out of the loop immediately. It is handy for searching:

FindFirst.java
public class FindFirst {
    public static void main(String[] args) {
        int[] numbers = {4, 9, 15, 22, 31};
        int found = -1;
        for (int n : numbers) {
            if (n % 5 == 0) {
                found = n;
                break;           // no need to look further
            }
        }
        System.out.println("First multiple of 5: " + found);
    }
}
Output
First multiple of 5: 15

continue: skip to the next iteration#

continue skips the rest of the current pass and moves on to the next:

SkipOdds.java
public class SkipOdds {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i % 2 != 0) continue;   // skip odd numbers
            if (i > 8) break;           // stop entirely after 8
            System.out.print(i + " ");
        }
        System.out.println();
    }
}
Output
2 4 6 8

Nested loops#

A loop inside another loop runs the inner loop completely for every pass of the outer loop. Classic uses are grids, tables and pattern printing:

Patterns.java
public class Patterns {
    public static void main(String[] args) {
        for (int row = 1; row <= 4; row++) {
            for (int col = 1; col <= row; col++) {
                System.out.print("* ");
            }
            System.out.println();
        }

        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                System.out.printf("%4d", i * j);
            }
            System.out.println();
        }
    }
}
Output
*
* *
* * *
* * * *
   1   2   3
   2   4   6
   3   6   9

%4d prints an integer right-aligned in a field 4 characters wide, which keeps the table neat. If the outer loop runs n times and the inner loop runs n times, the body runs n * n times, so nested loops get slow quickly on big inputs.

Labelled break and continue#

Plain break only exits the innermost loop. To exit an outer loop, put a label before it:

LabelledBreak.java
public class LabelledBreak {
    public static void main(String[] args) {
        int[][] grid = {
            {1, 2, 3},
            {4, -5, 6},
            {7, 8, 9}
        };
        search:
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[r].length; c++) {
                if (grid[r][c] < 0) {
                    System.out.println("Negative at row " + r + ", col " + c);
                    break search;   // leaves BOTH loops
                }
            }
        }
        System.out.println("Search finished");
    }
}
Output
Negative at row 1, col 1
Search finished

Labels are rarely needed. Often extracting the search into a method and using return is cleaner.

Choosing the right loop#

SituationLoop
Visit every element, no index neededenhanced for
Known number of repetitions, or you need the indexfor
Repeat until something happens (unknown count)while
Body must run at least once (menus, input prompts)do-while

A worked example: number-guessing game#

GuessGame.java
import java.util.Random;
import java.util.Scanner;

public class GuessGame {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int secret = new Random().nextInt(1, 101);   // 1..100
        int attempts = 0;
        int guess;

        do {
            System.out.print("Guess (1-100): ");
            guess = in.nextInt();
            attempts++;
            if (guess < secret) {
                System.out.println("Too low");
            } else if (guess > secret) {
                System.out.println("Too high");
            }
        } while (guess != secret);

        System.out.println("Correct! You needed " + attempts + " attempts.");
    }
}

new Random().nextInt(1, 101) (Java 17+) returns a random int from 1 up to, but not including, 101. With a good strategy (always guess the middle), you never need more than 7 attempts. That idea is called binary search.

Common mistakes#

  • Infinite loops: forgetting to update the counter, or updating the wrong variable.
  • Off-by-one: <= versus <, or starting at 1 instead of 0.
  • A stray semicolon: for (int i = 0; i < 5; i++); has an empty body, so the block below runs once.
  • Modifying a collection while looping over it with for-each: this throws ConcurrentModificationException (covered in the collections lessons).

What's next#

You can now repeat work. Next we organise code into reusable, named building blocks: methods.

Check your understanding

Quick quiz

0/3 answered
  1. 1.How many times does the body of for (int i = 0; i < 10; i += 3) run?

  2. 2.Which loop always runs its body at least once?

  3. 3.Inside nested loops, what does continue do?

Finished reading?

Mark this lesson complete to track your progress.