java-foundations 25 min read

Accumulator, Sentinel, and Choosing the Loop Shape

Two patterns that cover most real loops, and a decision tree for picking the shape

A loop can do more than count or print. Most of the loops you will write in this course collect something across iterations — a running sum, a running product, the largest value seen so far. The variable that holds the collected result is called an accumulator: it lives outside the loop, is initialized before the loop starts, and is updated in the body each pass.

A loop can also stop when the body tells it to — when the user types a special value, when a search finds what it was looking for, when a computation falls below a threshold. That’s the sentinel pattern. Combined with the “choose the loop shape” decision tree at the end, these two patterns cover most of what real-world Java 1 loops actually look like.

In a nutshell

The accumulator pattern has three parts: an initial value (the identity for your operation), an update inside the loop body, and a read after the loop ends. The initial value matters — 0 for sums, 1 for products, the first input for max/min.

The sentinel pattern has three parts: a priming read before the loop, a condition that checks whether the read was the sentinel, and a re-read at the end of the body so the loop always checks fresh input. The sentinel value itself is never processed — the loop exits before touching it.

The flag pattern uses a boolean to signal “stop.” When the stop condition is computed from state rather than matched against a single input value, declare a boolean done = false; outside the loop, loop while (!done), and let the body set done = true; once its work is finished. Reach for it when “we’re done” is too complex to fit cleanly in the loop header.

Choose the loop shape by asking three questions, in order: (1) must the body run at least once? → do-while. (2) Is the iteration count known up front? → for. (3) Does the body determine when to stop? → while (with a sentinel, a flag, or any computed condition). All three shapes can express any loop; you choose for readability.

Quick reference

Accumulator initial values (the identity rule)

Operation Initial value Reason
sum 0 0 + x = x
product 1 1 * x = x
max first input nothing is “less than” the first value
min first input nothing is “greater than” the first value
count 0 start with nothing counted

Sentinel pattern skeleton

int x = in.nextInt();       // priming read BEFORE the loop
while (x != SENTINEL) {
    // process x here
    x = in.nextInt();       // re-read at the END of the body
}

Flag pattern skeleton

boolean done = false;
while (!done) {
    // do the work for one pass
    if (/* whatever "we're done" means */) {
        done = true;
    }
}

The three-question decision tree

              Does the body run at least once?
                         |
              YES +-------+------- NO
                  |                |
             do-while       Is exact count known up front?
                                YES |  NO
                                    |   |
                                  for   Does the body determine termination?
                                             YES |   NO
                                                 |    |
                                              while  (re-read the problem)

Deep dive

1. The accumulator pattern: running sum

Plan. Sum N integers read from Scanner.

  • Declare int sum = 0; — the identity for addition.
  • Read N.
  • Loop N times. Body: read the next integer, add it to sum.
  • After the loop: print sum.
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int sum = 0;                         // accumulator; identity for addition
for (int i = 0; i < n; i++) {
    int x = in.nextInt();
    sum += x;                        // update the accumulator every pass
}
System.out.println("sum = " + sum);

Sample run (N = 4, then 10 20 30 40):

sum = 100

Invariant. At the top of iteration i, sum equals the total of all values read so far. That sentence is worth writing as a comment above the loop — it forces you to say exactly what the loop is doing.

2. Running product — same skeleton, different identity

Plan. Compute the product of N integers. Identity for multiplication is 1.

int product = 1;                     // identity for multiplication
for (int i = 0; i < n; i++) {
    int x = in.nextInt();
    product *= x;
}

If you initialize product = 0, every multiplication gives zero and you end up with 0 regardless of input. The identity rule is not a suggestion — it is the reason the accumulator works.

3. Running max — why the initial value matters

Finding the maximum uses the same shape: an accumulator, updated in the body. The update rule changes: compare, and keep the larger value.

Plan. Find the max of K inputs. Initialize max to the first input, then loop over the remaining K − 1.

Scanner in = new Scanner(System.in);
int k = in.nextInt();
int max = in.nextInt();              // prime: first input is the initial max
for (int i = 1; i < k; i++) {        // already read one; loop k-1 more
    int x = in.nextInt();
    if (x > max) {
        max = x;
    }
}
System.out.println("max = " + max);

Sample run (k = 3, then -5 -2 -9): max = -2.

Pitfall — initializing max to 0 when inputs can be negative. If you write int max = 0; and every input is negative, the body never updates max — every input is less than 0, so x > max is always false. The program prints max = 0, which is wrong. Failure signature: running max returns 0 when all inputs are negative. If you see that, check your initialization.

The fix is the first-input rule: initialize max to a real input value. No subsequent value can be “smaller than anything real,” because there is no “smaller than anything.”

4. Running min — the mirror

Replace > with <. Initialize min to the first input by the same rule.

int min = in.nextInt();
for (int i = 1; i < k; i++) {
    int x = in.nextInt();
    if (x < min) {
        min = x;
    }
}

5. The sentinel pattern

Sometimes you do not know how many values you will read. The user has a list, but they did not tell you in advance how long it is. You need a way for them to say “I’m done.” That signal is a sentinel — a value outside the valid input range that acts as a stop cue.

Pick a sentinel the problem guarantees will not appear as real data. For a list of positive integers, -1 works. For a list that could include -1, use -999 or whatever the spec reserves.

The priming-read idiom. Read one value before the loop. The loop condition tests whether that value is the sentinel. If it is not, process it — then read the next one at the end of the body. On the next condition check, the new value is tested.

Plan. Read integers, sentinel -1, compute the sum.

Scanner in = new Scanner(System.in);
int sum = 0;
System.out.print("enter a number (-1 to stop): ");
int x = in.nextInt();                        // priming read
while (x != -1) {
    sum += x;                                // process the real value
    System.out.print("enter a number (-1 to stop): ");
    x = in.nextInt();                        // re-read at end of body
}
System.out.println("sum = " + sum);

Sample run:

enter a number (-1 to stop): 10
enter a number (-1 to stop): 20
enter a number (-1 to stop): 5
enter a number (-1 to stop): -1
sum = 35

Key insight — the sentinel is never processed. When the user types -1, the condition x != -1 is false and the loop exits before the body runs. If you accidentally add the sentinel to the sum, it is because you placed the re-read at the top of the body rather than the bottom. Structure: read, check, process, re-read, check again.

What if the sentinel is typed first? The priming read sets x = -1. The condition -1 != -1 is false. The body never runs. sum stays 0 and the program prints sum = 0. That is correct — zero real values were entered. This is why the priming read sits outside the loop: the condition needs something to test on the first check.

Check Your Understanding
A student writes this sentinel loop to sum positive integers entered with sentinel 0, but it always returns a sum that is off by the first value entered. What is the bug?
int sum = 0;
int x;
while ((x = in.nextInt()) != 0) {
    x = in.nextInt();     // read next value
    sum += x;
}
AThe initial value of sum should be 1, not 0.
BEach pass reads twice — once in the condition, once in the body — so every other value is skipped.
CThe sentinel should be -1, not 0.
DThe condition should be x > 0.
Answer: B. The first in.nextInt() in the condition consumes a value that gets tested but then immediately overwritten by the second in.nextInt() in the body. Use the standard structure: one priming read before the loop, one re-read at the end of the body. Never read twice per pass.

6. The flag pattern — when the stop signal is computed, not read

The sentinel pattern works when one specific input value cleanly means “stop.” Sometimes the stop condition is more complex — a running total crosses a threshold, a search finds its target, two consecutive inputs are equal. The flag pattern uses a boolean variable as a switch: the body sets it to true when the work is finished, and the loop condition tests the flag.

Plan. Read integers and add them to a running sum. Stop as soon as the sum exceeds 100. Print how many integers it took.

Scanner in = new Scanner(System.in);
int sum = 0;
int count = 0;
boolean done = false;
while (!done) {
    sum += in.nextInt();
    count++;
    if (sum > 100) {
        done = true;
    }
}
System.out.println("Took " + count + " integers; sum = " + sum);

The condition !done reads as “while we are not done.” The body owns the decision to flip the flag. Once it does, the next condition check exits the loop.

Pitfall — flag set, but body keeps going. Setting done = true does not exit the loop on the spot. The flag only takes effect on the next condition check, so any code after the flag-set still runs in the current iteration. If you need to stop work mid-body, restructure the body so nothing follows the flag-set, or use break (lesson 4-e).

Sentinel vs. flag — how to choose. A sentinel works when one input value reliably means “stop” (-1 in a list of positive integers). A flag works when the stop condition is computed from accumulated state. Both produce while loops; the flag just makes a complex termination condition readable as English.

7. Choosing the loop shape

All three loop shapes can express any loop. You choose between them for readability. Here’s the decision tree applied to five English problem statements.

(1) “Print the integers from 1 to 100.” Body at least once? Not required. Count known up front? Yes, 100 iterations. for.

(2) “Read numbers until the user types -1; print the average.” Body at least once? No (user might type -1 immediately). Count known? No. Body determines termination? Yes. while (sentinel).

(3) “Keep doubling a number until it exceeds one million.” At least once? Possibly not (if the number already exceeds a million, zero iterations is correct). Count known? No. Body determines termination? Yes. while.

(4) “Read N temperatures from Scanner and find the maximum.” At least once? Not required. Count known? Yes, N. for (with first-input initialization for max).

(5) “Prompt for a positive integer; re-prompt until they give one.” At least once? Yes — you cannot validate what you have not read. do-while (lesson 4-e).

The do-while case usually sounds like “prompt until valid” or “retry until correct.” If the problem can complete in zero iterations (user was right the first time, or there is no input at all), it is not do-while. The distinguishing question: is the first iteration unconditional?

8. The five-step English-to-Java plan

Knowing the shape is half the job. Writing code from an English description is the other half. Use five steps, in order, every time.

  1. What varies each iteration? Name the loop variable.
  2. When do we stop? Write the continue condition as a boolean expression.
  3. What happens each time? Describe the body in one sentence.
  4. What gets tracked across iterations? List every variable that persists outside the loop (counters, accumulators, the priming-read variable).
  5. Which loop shape? Apply the decision tree.

Worked translation. “Read integers (sentinel -1), count how many are positive.”

// Step 1: What varies?      x, each integer read from Scanner
// Step 2: When stop?        x == -1; continue while x != -1
// Step 3: What each time?   if x > 0, increment count; re-read x
// Step 4: Tracked across?   count (0), x (priming read before loop)
// Step 5: Shape?            count unknown, body reads -> while + sentinel

Scanner in = new Scanner(System.in);
int count = 0;
int x = in.nextInt();                  // priming read
while (x != -1) {
    if (x > 0) {
        count++;
    }
    x = in.nextInt();                  // re-read at end of body
}
System.out.println("positive count = " + count);

Sample run (input 3 -7 5 0 -1): positive count = 2.


Before you leave

The accumulator pattern holds a running result across iterations — sum, product, max, min, count. The initial value is the identity for the operation, or the first input for max/min. The sentinel pattern reads until a stop value appears; it requires a priming read and a re-read at the end of the body, and the sentinel itself is never processed. The flag pattern is the sentinel’s cousin for cases where “we’re done” is computed from state rather than read from input. Choose the loop shape with the three-question decision tree: do-while for “at least once,” for for known counts, while when the body decides (sentinel, flag, or any computed termination).

Tomorrow: loops inside loops. The inner loop restarts from its own init every time the outer loop advances — that multiplication is what lets a few lines of nested for print a full grid.

Want to practice? The Week 4 quiz-prep set on the practice platform has accumulator and sentinel problems mapped to this lesson.