Java Nested Loops
Java Nested Loops
A nested loop is a loop placed inside the body of another loop. The inner loop completes all its iterations for every single iteration of the outer loop. That multiplication of iterations is both the power and the risk of nested loops — understanding what happens to execution count as the input grows is what separates developers who use nested loops correctly from those who introduce O(n²) problems where O(n) was achievable.
What Are Nested Loops in Java?
When you need to work with two-dimensional data — a grid, a matrix, a table, or combinations of two sets — nested loops express that naturally. The outer loop moves through rows (or the first dimension), and the inner loop processes each column (or the second dimension) for every row the outer loop visits.
Any loop type can be nested inside any other — for inside while, while inside for, for-each inside for. The choice of loop type at each level follows the same rules as single loops: use for when the count is known, while when it depends on runtime state, for-each when you need each element without an index.
Syntax
Basic Nested for Loop
1for (int outer = 0; outer < outerLimit; outer++) {
2 for (int inner = 0; inner < innerLimit; inner++) {
3 // executes outerLimit × innerLimit times total
4 }
5}Nested for-each
1for (ElementType outerElement : outerCollection) {
2 for (ElementType innerElement : innerCollection) {
3 // process each combination of outerElement and innerElement
4 }
5}Mixed — for inside while
1while (outerCondition) {
2 for (int inner = 0; inner < limit; inner++) {
3 // inner loop count is fixed; outer termination depends on runtime state
4 }
5}The iteration count of a two-level nested loop is the product of both loop counts. A nested loop where the outer runs 100 times and the inner runs 100 times executes 10,000 iterations — O(n²) when both limits scale with the same input size. This is the first thing to check during any code review involving nested loops.
Beginner Examples
Multiplication Table
The classic two-dimensional output problem — each cell is the product of its row and column position.
1public class MultiplicationTableDemo {
2
3 public static void main(String[] args) {
4
5 int tableSize = 5;
6
7 for (int row = 1; row <= tableSize; row++) {
8 for (int col = 1; col <= tableSize; col++) {
9 // printf pads each product to 4 characters to align columns
10 System.out.printf("%4d", row * col);
11 }
12 // Move to next line after each complete row
13 System.out.println();
14 }
15 }
16}Output:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
The outer loop runs 5 times. For each outer iteration, the inner loop runs 5 times. Total iterations: 25. The inner loop's index col resets to 1 on every new outer iteration — that reset is fundamental to how nested loops produce two-dimensional output.
Triangle Pattern
Pattern printing is a common interview exercise because it tests whether the candidate understands how the inner loop's range can depend on the outer loop's current value.
1public class TrianglePatternDemo {
2
3 public static void main(String[] args) {
4
5 int rows = 5;
6
7 for (int row = 1; row <= rows; row++) {
8 // Inner loop limit changes with each outer iteration
9 // Row 1 prints 1 star, row 2 prints 2 stars, etc.
10 for (int star = 1; star <= row; star++) {
11 System.out.print("* ");
12 }
13 System.out.println();
14 }
15 }
16}Output:
*
* *
* * *
* * * *
* * * * *
The inner loop limit is star <= row — it grows with the outer counter. This is the key insight in pattern programs: the inner loop does not need a fixed upper bound; it can depend on the outer loop variable. Total iterations here are 1 + 2 + 3 + 4 + 5 = 15, not 25 — this is O(n²/2) which is still O(n²) asymptotically.
Finding Pairs That Sum to a Target
This is where nested loops appear in real algorithmic problem-solving — checking every pair of elements from an array.
1public class PairSumDemo {
2
3 public static void main(String[] args) {
4
5 int[] prices = {150, 300, 450, 200, 350};
6 int targetBudget = 500;
7
8 System.out.println("Item pairs within budget of Rs. " + targetBudget + ":");
9
10 // Outer starts at 0, inner starts at outer+1 to avoid duplicate pairs
11 for (int first = 0; first < prices.length; first++) {
12 for (int second = first + 1; second < prices.length; second++) {
13 int combined = prices[first] + prices[second];
14 if (combined <= targetBudget) {
15 System.out.println(" Rs." + prices[first]
16 + " + Rs." + prices[second]
17 + " = Rs." + combined);
18 }
19 }
20 }
21 }
22}Output:
Item pairs within budget of Rs. 500:
Rs.150 + Rs.300 = Rs.450
Rs.150 + Rs.200 = Rs.350
Rs.150 + Rs.350 = Rs.500
Rs.300 + Rs.200 = Rs.500
Starting the inner loop at first + 1 avoids checking (A, B) and then (B, A) — they are the same pair. This halves the work compared to starting at 0. This pattern is the two-pointer or brute-force pair approach that appears frequently in interview problems.
How Nested Loops Work Internally
Execution Flow
The diagram below shows how the JVM processes a two-level nested loop across three outer and three inner iterations.
Outer iteration 1: Inner iteration 1 → body runs Inner iteration 2 → body runs Inner iteration 3 → body runs (inner exits) Outer iteration 2: Inner resets to 0 Inner iteration 1 → body runs Inner iteration 2 → body runs Inner iteration 3 → body runs (inner exits) Outer iteration 3: Inner resets to 0 Inner iteration 1 → body runs Inner iteration 2 → body runs Inner iteration 3 → body runs (inner exits, outer exits) Total body executions: 3 × 3 = 9
Every time the outer loop advances one step, the inner loop runs completely from its initial value to its exit condition. The inner loop variable resets independently of the outer loop variable on every outer iteration.
Time Complexity Impact
1public class ComplexityDemo {
2
3 public static void main(String[] args) {
4
5 int inputSize = 100;
6 long singleLoopCount = 0;
7 long nestedLoopCount = 0;
8
9 // Single loop — O(n)
10 for (int i = 0; i < inputSize; i++) {
11 singleLoopCount++;
12 }
13
14 // Nested loop — O(n²)
15 for (int i = 0; i < inputSize; i++) {
16 for (int j = 0; j < inputSize; j++) {
17 nestedLoopCount++;
18 }
19 }
20
21 System.out.println("Input size : " + inputSize);
22 System.out.println("Single loop runs : " + singleLoopCount);
23 System.out.println("Nested loop runs : " + nestedLoopCount);
24 System.out.println("Ratio : " + (nestedLoopCount / singleLoopCount) + "x");
25 }
26}Output:
Input size : 100
Single loop runs : 100
Nested loop runs : 10000
Ratio : 100x
At n=100, the nested loop executes 100 times more than the single loop. At n=1000, it executes 1000 times more. This quadratic growth is why nested loops over large datasets — product catalog comparisons, user-recommendation matrix generation, naive string matching — become severe performance problems in production. A two-level nested loop is only acceptable when either the inner or outer iteration count is provably small and bounded.
Real-World Example — Order-Inventory Matching System
The Business Problem
You are building the inventory fulfillment engine for a quick-commerce platform — similar to what Zepto or Blinkit runs for its dark store management. When a batch of orders arrives, the system must check each order against each available inventory slot to find the best warehouse that can fulfill it. This involves checking every order against every warehouse's stock — a genuine two-dimensional traversal where each combination must be evaluated.
This is one of the scenarios where a nested loop is legitimately correct at small scale — the number of warehouses in a dark store network is bounded (typically 10 to 50), so the O(orders × warehouses) cost stays manageable.
Implementation
1// File: FulfillmentConfig.java
2
3public final class FulfillmentConfig {
4
5 public static final double MAX_DELIVERY_RADIUS_KM = 5.0;
6
7 private FulfillmentConfig() {}
8}1// File: InventorySlot.java
2
3public class InventorySlot {
4
5 private final String warehouseId;
6 private final String productSku;
7 private final int availableUnits;
8 private final double distanceFromCustomerKm;
9
10 public InventorySlot(String warehouseId, String productSku,
11 int availableUnits, double distanceFromCustomerKm) {
12 this.warehouseId = warehouseId;
13 this.productSku = productSku;
14 this.availableUnits = availableUnits;
15 this.distanceFromCustomerKm = distanceFromCustomerKm;
16 }
17
18 public String getWarehouseId() { return warehouseId; }
19 public String getProductSku() { return productSku; }
20 public int getAvailableUnits() { return availableUnits; }
21 public double getDistanceFromCustomerKm() { return distanceFromCustomerKm; }
22}1// File: FulfillmentMatcher.java
2
3import java.util.List;
4
5public class FulfillmentMatcher {
6
7 public void matchOrdersToInventory(
8 List<String> orderSkus,
9 List<InventorySlot> inventory) {
10
11 System.out.println("=== Fulfillment Matching Report ===");
12
13 // Outer loop — each order in the batch
14 for (String orderSku : orderSkus) {
15
16 boolean fulfilled = false;
17 System.out.println("\nOrder SKU: " + orderSku);
18
19 // Inner loop — each warehouse slot checked against the current order
20 for (InventorySlot slot : inventory) {
21
22 boolean skuMatches = slot.getProductSku().equals(orderSku);
23 boolean inStock = slot.getAvailableUnits() > 0;
24 boolean withinRadius = slot.getDistanceFromCustomerKm()
25 <= FulfillmentConfig.MAX_DELIVERY_RADIUS_KM;
26
27 if (skuMatches && inStock && withinRadius) {
28 System.out.println(" Fulfilled from: " + slot.getWarehouseId()
29 + " | Units available: " + slot.getAvailableUnits()
30 + " | Distance: " + slot.getDistanceFromCustomerKm() + " km");
31 fulfilled = true;
32 break; // first matching warehouse found — no need to check further
33 }
34 }
35
36 if (!fulfilled) {
37 System.out.println(" UNFULFILABLE: No matching in-range stock found.");
38 }
39 }
40 }
41}1// File: FulfillmentDemo.java
2
3import java.util.List;
4
5public class FulfillmentDemo {
6
7 public static void main(String[] args) {
8
9 List<String> incomingOrders = List.of(
10 "MILK-1L-ORG", "BREAD-WW-400G", "OIL-SUNF-1L", "EGGS-FARM-12"
11 );
12
13 List<InventorySlot> warehouseSlots = List.of(
14 new InventorySlot("WH-NORTH", "MILK-1L-ORG", 24, 2.1),
15 new InventorySlot("WH-SOUTH", "MILK-1L-ORG", 8, 6.8),
16 new InventorySlot("WH-NORTH", "BREAD-WW-400G", 15, 2.1),
17 new InventorySlot("WH-EAST", "OIL-SUNF-1L", 0, 1.4),
18 new InventorySlot("WH-CENTRAL","OIL-SUNF-1L", 5, 9.2),
19 new InventorySlot("WH-WEST", "EGGS-FARM-12", 30, 3.7)
20 );
21
22 FulfillmentMatcher matcher = new FulfillmentMatcher();
23 matcher.matchOrdersToInventory(incomingOrders, warehouseSlots);
24 }
25}Output:
=== Fulfillment Matching Report ===
Order SKU: MILK-1L-ORG
Fulfilled from: WH-NORTH | Units available: 24 | Distance: 2.1 km
Order SKU: BREAD-WW-400G
Fulfilled from: WH-NORTH | Units available: 15 | Distance: 2.1 km
Order SKU: OIL-SUNF-1L
UNFULFILABLE: No matching in-range stock found.
Order SKU: EGGS-FARM-12
Fulfilled from: WH-WEST | Units available: 30 | Distance: 3.7 km
The OIL-SUNF-1L order is unfulfilable because WH-EAST has zero stock and WH-CENTRAL is outside the 5 km radius. The break inside the inner loop exits as soon as a matching warehouse is found — there is no reason to keep scanning once fulfillment is confirmed. Without that break, every order would always check all warehouses even after a match was found, wasting time on all but the last warehouse in the list.
Using break and Labels in Nested Loops
Breaking the Inner Loop Only
1public class InnerBreakDemo {
2
3 public static void main(String[] args) {
4
5 for (int batch = 1; batch <= 3; batch++) {
6 System.out.print("Batch " + batch + ": ");
7
8 for (int item = 1; item <= 5; item++) {
9 if (item == 3) {
10 // Exits inner loop only — outer loop continues
11 break;
12 }
13 System.out.print(item + " ");
14 }
15 System.out.println();
16 }
17 }
18}Output:
Batch 1: 1 2
Batch 2: 1 2
Batch 3: 1 2
break without a label exits only the innermost loop. The outer loop continues normally. Each batch still processes items 1 and 2 before hitting the break at item 3.
Breaking the Outer Loop Using a Label
When a match is found in a nested search and the entire search must stop — not just the inner pass — a labeled break exits the outer loop directly.
1public class LabeledBreakDemo {
2
3 public static void main(String[] args) {
4
5 int[][] salesGrid = {
6 {120, 340, 890},
7 {210, 550, 100},
8 {670, 430, 820}
9 };
10
11 int targetSales = 550;
12 boolean found = false;
13
14 // Label on the outer loop
15 outerSearch:
16 for (int region = 0; region < salesGrid.length; region++) {
17 for (int quarter = 0; quarter < salesGrid[region].length; quarter++) {
18 if (salesGrid[region][quarter] == targetSales) {
19 System.out.println("Target Rs." + targetSales + "k found at:"
20 + " Region " + region + ", Quarter " + (quarter + 1));
21 found = true;
22 break outerSearch; // exits both loops immediately
23 }
24 }
25 }
26
27 if (!found) {
28 System.out.println("Target not found in any region.");
29 }
30 }
31}Output:
Target Rs.550k found at: Region 1, Quarter 2
Without break outerSearch, the inner break would only exit the inner loop and the outer loop would continue scanning the remaining regions unnecessarily. Labels are the only way to exit an outer loop from within an inner one in Java.
Best Practices
Keep nesting depth to two levels maximum
Three or more levels of nesting — a loop inside a loop inside a loop — produces code that is exponentially harder to read and reason about. When three levels feel necessary, extract the inner two levels into a private method. The calling code stays clean; the inner logic becomes independently testable.
Always check whether an O(n²) solution is acceptable for your data size
Before writing a nested loop over a collection, ask: what is the maximum size of the outer and inner collections? A nested loop over two collections of 100 items runs 10,000 iterations. Over 10,000 items, it runs 100 million iterations. For large datasets, consider whether a HashMap lookup can replace the inner loop — reducing O(n²) to O(n).
Break out of inner loops as soon as a result is found
If the inner loop is searching for a match, add break the moment the match is found. Continuing to scan after a result is already known wastes CPU time proportionally to how early in the dataset the match appears.
Give outer and inner loop variables meaningful names
Single-letter loop counters like i and j are conventional, but in nested loops they become ambiguous at a glance. Using rowIndex and colIndex, or regionIndex and quarterIndex, makes the purpose of each level clear without requiring comments.
Common Mistakes
Mistake 1 — Using the Same Loop Variable Name at Both Levels
1// This is a compile-time error — 'i' is already declared in the enclosing scope
2for (int i = 0; i < 3; i++) {
3 for (int i = 0; i < 3; i++) { // 'i' is already defined — compiler rejects this
4 System.out.println(i);
5 }
6}Java requires each loop variable in a nested structure to have a unique name within its scope. Using the same name at two levels is a compile-time error. Using names like outerIndex and innerIndex avoids this entirely and adds clarity.
Mistake 2 — Off-by-One in the Inner Loop Bound
1public class InnerOffByOneDemo {
2
3 public static void main(String[] args) {
4
5 String[] teams = {"Alpha", "Beta", "Gamma"};
6 String[] projects = {"Phoenix", "Atlas", "Nova"};
7
8 // Inner loop uses <= instead of < — accesses index 3, which does not exist
9 try {
10 for (int t = 0; t < teams.length; t++) {
11 for (int p = 0; p <= projects.length; p++) {
12 System.out.println(teams[t] + " → " + projects[p]);
13 }
14 }
15 } catch (ArrayIndexOutOfBoundsException ex) {
16 System.out.println("ArrayIndexOutOfBoundsException at inner index: "
17 + projects.length);
18 }
19 }
20}Output:
Alpha → Phoenix
Alpha → Atlas
Alpha → Nova
ArrayIndexOutOfBoundsException at inner index: 3
The inner loop's <= allows p to reach 3 on a zero-indexed array of length 3. The valid range is 0 through 2. The rule is always < array.length for zero-indexed iteration. This mistake is more common in nested loops because the inner bound is visually farther from the array declaration.
Mistake 3 — Unintentional O(n²) Over Large Data
1import java.util.List;
2import java.util.ArrayList;
3
4public class QuadraticMistakeDemo {
5
6 public static void main(String[] args) {
7
8 // Simulating a large product catalog and a large wishlist
9 List<String> catalog = new ArrayList<>();
10 List<String> wishlist = new ArrayList<>();
11
12 for (int i = 0; i < 1000; i++) catalog.add("SKU-" + i);
13 for (int i = 990; i < 1000; i++) wishlist.add("SKU-" + i);
14
15 long iterations = 0;
16
17 // O(n × m) — 1000 × 10 = 10,000 iterations for this small case
18 // If catalog were 100,000 items, this becomes 1,000,000 iterations
19 for (String wished : wishlist) {
20 for (String product : catalog) {
21 iterations++;
22 if (product.equals(wished)) {
23 // found — should break here to avoid the rest of the inner loop
24 }
25 }
26 }
27
28 System.out.println("Total iterations: " + iterations);
29 System.out.println("A HashSet lookup would do this in ~" + wishlist.size() + " iterations.");
30 }
31}Output:
Total iterations: 10000
A HashSet lookup would do this in ~10 iterations.
Loading the catalog into a HashSet<String> and using catalogSet.contains(wished) reduces the inner loop entirely — O(n × m) becomes O(n) with O(m) setup cost. During code reviews, nested loops over large collections are the first place seniors look for avoidable quadratic complexity.
Interview Questions
Q1. What is a nested loop in Java and how does the iteration count work?
A nested loop is a loop placed inside the body of another loop. The inner loop completes all its iterations for every single iteration of the outer loop. The total iteration count is the product of both loop counts — a nested loop where the outer runs M times and the inner runs N times executes M × N body iterations total. When both M and N scale with the same input size n, the loop is O(n²) — this is the most important characteristic to understand before writing any nested loop.
Q2. How do you exit both loops in a nested loop using break?
A plain break exits only the innermost loop it appears in. To exit an outer loop from inside an inner loop, use a labeled break. You assign a label to the outer loop — outerLabel: — and then break outerLabel; inside the inner loop exits the labeled loop directly. This is the only mechanism in Java for breaking out of more than one loop level simultaneously without using a flag variable or restructuring into a method with return.
Q3. What is the time complexity of a nested loop and when is it acceptable?
A two-level nested loop where both loops scale with the same input size is O(n²). This is acceptable when either the outer or inner iteration count is provably small and bounded — for example, iterating 50 warehouses for each of 200 orders is 10,000 iterations, which is fine. It is not acceptable when both sizes scale with user input — searching through a 100,000-item catalog for each of 50,000 wishlist items produces 5 billion iterations. For large collections, a HashMap or HashSet lookup inside a single loop reduces the complexity to O(n).
Q4. What is a labeled break in Java and when would you use it?
A labeled break exits the loop identified by its label rather than the innermost enclosing loop. You use it when a match found in an inner loop should immediately stop the entire nested search — for example, finding a row and column in a 2D grid, locating a matching record in a nested collection scan, or stopping a multi-level search once the first valid result is found. Without labels, you would need a boolean flag in the outer loop condition to signal early exit, which is more verbose.
Q5. How would you improve a naive O(n²) nested loop search to O(n)?
Load the inner collection into a HashSet before the outer loop begins. Replace the inner loop with a single contains() call on the HashSet, which is O(1) on average. The total complexity becomes O(n) for the outer loop plus O(m) to build the set — O(n + m) instead of O(n × m). This is the standard optimization that converts pair-searching nested loops into single-pass lookups and is one of the most commonly tested refactoring scenarios in product-based company interviews.
Q6. Can you nest different types of loops in Java?
Yes. Any loop type can be nested inside any other — for inside while, while inside for-each, do-while inside for, and so on. The choice of loop type at each level is independent. Use the loop type that best expresses the iteration intent at that level: for when the count is known, while when termination depends on runtime state, for-each when reading every element without an index.
FAQs
What is a nested loop in Java?
A nested loop is a loop placed inside another loop's body. The inner loop runs completely for every single iteration of the outer loop, producing a total iteration count equal to the product of both loops' iteration counts.
How many levels of nesting are allowed in Java?
Java places no syntactic limit on nesting depth. However, more than two levels is consistently flagged in code reviews as a readability and maintainability problem. Three-level nesting almost always signals that the inner logic should be extracted into a separate method.
What is a labeled break in Java nested loops?
A labeled break exits the loop associated with a specific label — not just the innermost loop. You place the label directly before the outer loop, then write break labelName; inside any inner loop to exit the labeled outer loop immediately.
What time complexity does a nested loop have?
A two-level nested loop is O(n²) when both loop counts scale with the same input size. If the inner loop has a fixed, bounded count, the complexity remains O(n). Always assess the data sizes before writing nested loops over large collections.
How do you break out of both loops at once in Java?
Use a labeled break. Apply a label to the outer loop — outerLoop: — and write break outerLoop; inside the inner loop. This exits both loops immediately without flag variables or restructuring.
Can the inner loop variable depend on the outer loop variable?
Yes. The inner loop's initialization, condition, and update can all reference the outer loop variable. This is exactly how triangular patterns work — the inner loop runs row times on outer iteration row, producing a triangularly shaped output instead of a rectangular one.
Summary
Nested loops express two-dimensional iteration — working through every combination of two sets, processing every cell in a grid, or checking every pair in a collection. The inner loop always resets and runs completely for each outer iteration, and the total execution count is their product.
Two things define whether nested loops are used well in production code. First, the time complexity must be acceptable for the data size — an O(n²) nested loop over bounded, small collections is fine; the same loop over user-scaled data is a performance problem. Second, break exits only the innermost loop, and labeled break is the correct mechanism for exiting outer loops from inside inner ones.
For interviews, be ready to calculate the iteration count of a two-level nested loop, explain why and how you would optimize a nested loop search to O(n) using a HashSet, and demonstrate labeled break with a concrete example. These three points cover the depth that both service-based recall and product-based optimization questions probe on this topic.
What to Read Next
| Topic | Link |
|---|---|
| How the for loop works and when to use it for single-level iteration | Java for Loop → |
| How break and continue control early exit and iteration skipping | Java break and continue → |
| How arrays store two-dimensional data for use with nested loops | Java Arrays → |
| How the while loop handles iteration when the exit depends on runtime state | Java while Loop → |
| How HashMap provides O(1) lookup to replace inner loop searches | Java HashMap → |