Skip to content
Study Hall
Lesson 2 of 3Looping
Unit 4 · Lesson 2 of 3

for Loops and Counting

The counting loop taken apart piece by piece, why programmers start at 0, and the off-by-one error that comes from one wrong character.

5 min read11 practice questionsAsk AI about this →

Key things to remember

  1. 1for (init; condition; update) — the initialization runs once, then the cycle is test, body, update, test.
  2. 2for (int i = 0; i < n; i++) runs exactly times, with taking the values to .
  3. 3Counting by 1 from to inclusive runs times.
  4. 4A variable declared in the for header exists only inside the loop — using it afterwards will not compile.
  5. 5Use for when the number of passes is known in advance, while when it is not.
  6. 6The enhanced for loop for (int x : arr) gives you each value but no index.

A while loop scatters its three parts across three different places: the initialization sits above the loop, the condition sits in the header, and the update hides somewhere in the body. That is fine until the body grows and you can no longer see them at a glance. A for loop gathers all three onto one line, which makes a counting loop readable in a single glance and makes a forgotten update almost impossible.

for (int i = 0; i < 5; i++) {
    System.out.println(i);
}
Prints 0, 1, 2, 3, 4 — five lines, one for each pass.
PieceNameWhen it runs
int i = 0InitializationOnce only, before anything else
i < 5ConditionBefore every pass, including the first
System.out.println(i)BodyOnly when the condition was true
i++UpdateAfter the body, before the condition is re-tested
The order is init, test, body, update, test, body, update … and the loop exits from a failed test.
The update runs after the body, not before
This is the detail students get wrong when hand-tracing. On the very first pass, the body sees i at its starting value, because i++ has not happened yet. The increment only fires once the last statement of the body has finished. That is why a loop counting from 0 to 4 prints 0 first and not 1.
A for loop is a while loop with the three parts collected in one place.while — parts scatteredfor — parts gathered in one lineint i = 1;while (i <= 5) { sum += i; i++;}initconditionupdatefor (int i = 1; i <= 5; i++) { sum += i;}Same five passes: i = 1, 2, 3, 4, 5.i exists only inside the loop here.i still exists after the loop ends.initialise — once, firstcondition — before each passupdate — after each passUse for when you already know how many passes you need (counting, walking an array).Use while when you do not — "keep asking until the user types a valid mark".
Fig 1The same loop written both ways: identical behaviour, different amount of room for error.
AReach for for when…
  • You know the number of passes before you start
  • You are stepping through positions 0 to n-1
  • You are counting up, down, or by a fixed step
  • Repeat exactly 12 times
  • Print every item in an array
BReach for while when…
  • The number of passes is unknown in advance
  • The loop ends on an event, not a count
  • The control variable is not a simple counter
  • Keep reading until the user types a sentinel
  • Keep halving until the value drops below 1

Why programmers count from 0

Starting at 0 looks unnatural for about a week and then becomes the only thing that makes sense. Java numbers array positions from 0, so an array of 5 items has positions 0, 1, 2, 3, 4 — and for (int i = 0; i < 5; i++) hits exactly those and nothing else. The bound and the length are then the same number, so i < marks.length is always right and you never have to remember to subtract one. Read i < n as "how many" and i <= n as "how far": starting at 0, the condition i < n runs the body n times, while starting at 1, the condition i <= n also runs it n times. Pick one pairing and stay with it, because mixing them is how off-by-one errors are born.

// Counting down: start high, end low, and decrement
for (int i = 10; i >= 1; i--) {
    System.out.print(i + " ");
}
System.out.println("Liftoff!");
// Output: 10 9 8 7 6 5 4 3 2 1 Liftoff!

// Counting by a step other than 1
for (int i = 0; i <= 20; i += 5) {
    System.out.print(i + " ");
}
// Output: 0 5 10 15 20
The countdown runs 10 times and the step-5 loop runs 5 times. When you reverse a loop's direction you must flip the comparison too — changing only i++ to i-- gives either zero passes or an infinite loop.

Off-by-one errors

An off-by-one error is a loop that runs one time too many or one time too few, and it is the most common bug in all of programming. It comes down to one character: < instead of <=, or a starting value of 0 instead of 1. The code compiles, looks correct, and produces an answer that is nearly right — which is worse than an obvious crash, because you may never notice.

One character decides whether your loop fits the array or falls off the end.int[] a = {4, 8, 15, 16, 23}; // a.length is 5for (int i = 0; i < a.length; i++)— correct408115216323455 passes: i = 0, 1, 2, 3, 4stops as soon as i reaches 5every index existsfor (int i = 0; i <= a.length; i++)— one too many4081152163234?56 passes: i = 0 up to 5index 5 does not existArrayIndexOutOfBoundsValid indexes run 0 to length − 1, so the last index is always one less than the length. Use < with .length, every time.
Fig 2The same bound with four different headers, and the four different counts they produce.
HeaderValues taken by iNumber of passes
for (int i = 0; i < 5; i++)0, 1, 2, 3, 45
for (int i = 0; i <= 5; i++)0, 1, 2, 3, 4, 56
for (int i = 1; i <= 5; i++)1, 2, 3, 4, 55
for (int i = 1; i < 5; i++)1, 2, 3, 44
Four nearly identical headers; three different answers. The start value and the comparison must be chosen together.
The counting rule
A loop stepping by 1 from a up to and including b runs b minus a plus 1 times. If the condition stops before b, it runs b minus a times. For any other fixed step, divide the difference between the last and first values by the step and add 1. Substituting the real numbers into that rule takes five seconds and settles the argument without running the program.

When you only want each value in a collection and do not care about its position, Java offers a shorter form, read as "for each mark in marks". It cannot go out of bounds, because you never write an index, and it cannot run the wrong number of times. The trade-off is that you have no index to work with, so you cannot say "compare this item to the one before it" or write a new value back into the array.

int[] marks = {78, 85, 92, 64};

// Enhanced for: one pass per element, no index in sight
for (int mark : marks) {
    System.out.println(mark);
}

// The indexed form, which you need when the position matters
for (int i = 0; i < marks.length; i++) {
    System.out.println("Mark " + (i + 1) + " is " + marks[i]);
}
Both loops run 4 times. Only the second one knows where it is.

Worked examples

Reveal one step at a time and try to beat it to the next line.

Example 1
How many times does for (int i = 3; i <= 12; i += 3) run, and what is the value of i when the loop ends?
Example 2
Rewrite this while loop as a for loop, and give the final sum: int i = 1; int sum = 0; while (i <= 5) { sum += i; i++; }
Example 3
A loop is meant to print the 12 times table from 1 to 12 but prints 13 lines. Diagnose and fix for (int i = 1; i <= 13; i++) { System.out.println(i + " x 12 = " + (i * 12)); }
Example 4
Write a loop that prints every even number from 2 to 30 inclusive, and state how many numbers it prints.

Where marks get lost

  • Using <= where < belongs when the counter starts at 0 — the loop runs one extra time, and on an array that is an ArrayIndexOutOfBoundsException.
  • Writing a semicolon after the for header: for (int i = 0; i < 5; i++); — the loop runs five empty passes and the block below runs once.
  • Reversing a loop by changing only i++ to i-- and leaving the condition alone, which gives either zero passes or an infinite loop.
  • Changing the counter inside the body as well as in the update, so it advances by two each pass and the trace no longer matches the header.
  • Declaring int i in the for header and then trying to use i after the loop — the variable no longer exists there, so it will not compile.

Flashcards 10

Name the three parts of a for header, in order.
When does the update in a for loop run?
How many times does for (int i = 0; i < n; i++) run?
How many times does for (int i = 0; i <= n; i++) run?
+ 6 more

Practice

1
How many times does the body of for (int i = 0; i < 8; i++) run?
2
How many times does the body of for (int i = 0; i <= 8; i++) run?
3
How many times does the body of for (int i = 5; i <= 50; i += 5) run?
4
How many times does the body of for (int i = 10; i > 0; i -= 2) run?
5
What is printed first by for (int i = 0; i < 3; i++) { System.out.println(i); }?
6
Which loop should you write to keep reading numbers until the user types 0?
7
Why can an enhanced for loop for (int mark : marks) never throw an ArrayIndexOutOfBoundsException?
8
Put the steps of for (int i = 0; i < 3; i++) in the order Java performs them when starting the loop and completing one full pass.
  1. 1Test the condition i < 3 again
  2. 2Run the update i++
  3. 3Run the body
  4. 4Run the initialization: int i = 0
  5. 5Test the condition i < 3
9
Match each header to the number of passes it produces.
for (int i = 0; i < 6; i++)
for (int i = 1; i <= 6; i++)
for (int i = 0; i <= 6; i++)
for (int i = 1; i < 6; i++)
10
What is the name for a bug where a loop runs exactly one pass too many or too few?
11
A loop over an array of 6 marks crashes with ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 6. What is the fix?

Spot a mistake on this page? Let us know