Java Loop Patterns: Two Pointers, Sliding Window and More
The loop patterns behind most Java programs: accumulate, count, find, all and any, max and min, two pointers, sliding window, pairs and sentinels.
- Course: Java study plan
- Module: Control flow
- Kind: Lesson
- Reading time: 14 min
- Runtime: OpenJDK 18
What are the common loop patterns in Java?
The common loop patterns in Java are accumulate, a sum or product seeded with its identity value; count; find the first match with an early exit; all or any; best so far, for a maximum or minimum; two pointers; a sliding window; nested loops over pairs; and reading until a sentinel or end of input. Most interview problems combine two of them.
Lesson
Most loops are one of a dozen patterns: accumulate, count, find, check all or any, track a best, walk two things at once, generate pairs, read until a sentinel. Recognising the pattern lets you write the loop without thinking about it — and lets an interviewer see that you have. This lesson names them, shows the canonical form of each, and points out the boundary and initial-value traps in each.
Accumulate
long sum = 0;
for (int v : values) sum += v;
double product = 1.0;
for (double f : factors) product *= f;
StringBuilder sb = new StringBuilder();
for (String s : parts) sb.append(s);
The trap is the identity value: 0 for sums, 1 for products, an empty builder for concatenation. And the type: a sum of ints needs a long accumulator when it can overflow (Module 2).
Count
int evens = 0;
for (int v : values) if (v % 2 == 0) evens++;
A count is an accumulate of 1s under a condition. A count over characters, words, or matches is the same shape.
Find (first match)
int index = -1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) { index = i; break; }
}
Or, better, as a method with return i; and return -1; after the loop. The "not found" value must be one that cannot be a real answer: -1 for indices, null for objects (or Optional, Module 15).
All / any / none
boolean allPositive = true;
for (int v : values) if (v <= 0) { allPositive = false; break; }
boolean anyNegative = false;
for (int v : values) if (v < 0) { anyNegative = true; break; }
Note the initial values: all starts true and looks for a counterexample; any starts false and looks for a witness. Both stop early. On an empty input, all is vacuously true and any is false — the mathematically right answers, and sometimes a surprise. As methods, these are return false; inside the loop and return true; after (for all), and the reverse for any.
Best so far (max / min / argmax)
int max = Integer.MIN_VALUE;
int maxIndex = -1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) { max = arr[i]; maxIndex = i; }
}
Two ways to seed: with the type's extreme (Integer.MIN_VALUE for a max) or with the first element (max = arr[0], loop from 1 — but then an empty array must be handled first). Seeding with 0 is the classic bug: an all-negative array reports a max of 0. > versus >= decides whether ties keep the first or the last occurrence — say which you want.
Two pointers
int lo = 0, hi = arr.length - 1;
while (lo < hi) {
if (arr[lo] + arr[hi] == target) return true;
if (arr[lo] + arr[hi] < target) lo++; else hi--;
}
Two indices moving toward each other (palindromes, pair sums in a sorted array, reversing in place) or in the same direction at different speeds (removing duplicates, sliding windows). The loop condition is lo < hi for "distinct pair", lo <= hi for "may meet".
Sliding window
int best = 0, windowSum = 0;
for (int i = 0; i < arr.length; i++) {
windowSum += arr[i];
if (i >= k) windowSum -= arr[i - k]; // drop the element that left the window
if (i >= k - 1) best = Math.max(best, windowSum);
}
A fixed-size window over an array in one pass: add the incoming element, subtract the outgoing one. The two ifs handle the warm-up while the window is still filling.
Pairs and combinations
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++) // j starts at i + 1: each unordered pair once, no (i, i)
…
j = 0 gives ordered pairs including self-pairs; j = i includes (i, i); j = i + 1 gives each pair once. Getting this bound wrong double-counts or misses the diagonal.
Sentinel and end-of-input
String line;
while ((line = br.readLine()) != null) { … } // stream ends with null
int x;
while ((x = in.nextInt()) != 0) { … } // input ends with a 0 sentinel
while (in.hasNextInt()) { process(in.nextInt()); } // no sentinel: test availability
The assignment-in-condition idiom (x = read()) != END reads and tests in one step; the parentheses around the assignment are mandatory because != binds tighter than =.
Parallel iteration
for (int i = 0; i < Math.min(a.length, b.length); i++) {
use(a[i], b[i]);
}
Two sequences in step: index both with one variable, and decide what happens when the lengths differ (stop at the shorter, or check they match up front).
Running state
int prev = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < prev) return false; // not sorted
prev = arr[i];
}
Comparing each element with the previous one — sortedness, run detection, deltas. Start at index 1 and seed prev from index 0, after handling the empty case.
Do-something-n-times
for (int i = 0; i < n; i++) sb.append('*');
When the index is unused, some codebases write for (int i = 0; i < n; i++) anyway; "*".repeat(n) and IntStream.range(0, n) are the modern alternatives for the common cases.
Choosing the shape
| Need | Shape |
|---|---|
| A total, a product, a concatenation | accumulate with the right identity |
| How many satisfy X | count |
| The first that satisfies X | find with early exit, return -1/null |
| Do all / does any satisfy X | all (start true) / any (start false), early exit |
| The largest/smallest and where | best-so-far seeded from the first element or the extreme |
| A pair with a property in sorted data | two pointers |
| Best fixed-length run | sliding window |
| Every pair | nested with j = i + 1 |
| Read until done | sentinel / null / hasNext |
Almost every "medium" interview problem is two of these composed. Streams (Module 15) give one-line versions of accumulate, count, find, all/any and max — knowing the loop forms is what lets you read and debug the stream ones.
Key takeaways
- Seed accumulators with the identity, maxima from the first element or the extreme — never 0.
allstarts true and hunts for a counterexample;anystarts false and hunts for a witness; both exit early.- Pairs:
j = i + 1. Neighbours: start at 1 withprev. Windows: add incoming, drop outgoing. (x = read()) != ENDneeds its parentheses.
Common questions
How do I find the maximum value in an array in Java?
Seed max with the first element and loop from index 1, or seed it with Integer.MIN_VALUE, and replace it whenever arr[i] > max. Never seed with 0, or an all-negative array reports 0. Comparing with > keeps the first of tied values and >= the last.
What is the two-pointer technique?
Two indices move through an array together, usually from both ends toward each other in a while (lo < hi) loop, to check palindromes, find a pair with a given sum in sorted data or reverse in place. Moving them in the same direction at different speeds removes duplicates.
What is the sliding window technique?
A sliding window keeps a running result over a fixed-length run of elements in one pass: add the element entering the window and subtract the one leaving it, instead of re-summing every window. It finds the best sum of k consecutive elements in linear time.
How do I loop over every pair of elements once?
Nest two loops and start the inner one at i + 1: for (int i = 0; i < n; i++) for (int j = i + 1; j < n; j++). That visits each unordered pair exactly once, never pairing an element with itself; starting j at 0 gives ordered pairs, self-pairs included.
Why do all and any loops start with true and false?
An all-check assumes every element passes and hunts for a counterexample, so it starts true and becomes false at the first failure; an any-check starts false and hunts for a single witness. Both can stop early, and on an empty input all is true and any is false.
Exercises
Four patterns, one pass each
Read integers n and k (1 ≤ k ≤ n), then n integers. Print four lines:
max=<largest value> at=<index of its FIRST occurrence>run=<length of the longest non-decreasing run>(consecutive elements where each is ≥ the previous)window=<largest sum of k consecutive elements>allPositive=<true|false>
Seed the max from the first element, not from 0.
Example: 6 2 then 3 -1 4 4 5 2 →
max=5 at=4
run=4
window=9
allPositive=falseRuns, searches and thresholds
Read an integer n, n integers, and a threshold t. Print three lines, each one loop pattern:
run: value <v> length <k>— the longest run of equal adjacent values (the first such run on ties).firstNegative: <index or -1>— a search with early exit.untilOver: <k>— how many elements, from the start, are needed before the running sum exceedst(nif it never does).
Example: 7 then 3 3 -1 5 5 5 2 then 10 →
run: value 5 length 3
firstNegative: 2
untilOver: 5In this module: Control flow
← break, continue, labels and return · Checkpoint — Control flow →