Back to Blog
Java

Java Nested For Loop Explained with Examples

java nested for loop: Understand Java nested for loops: syntax, execution order, common use cases, performance costs, and pitfalls to avoid.

nested loopsJava loopsloop performancetime complexitymultidimensional arrays
Diagram of nested for loops showing outer loop controlling inner loop iterations

A Java nested for loop is a loop that runs inside another loop. The inner loop executes completely for each iteration of the outer loop, which multiplies the total number of iterations. This structure is common when processing two-dimensional arrays, generating combinations, or implementing algorithms that require pairwise comparisons. The syntax is straightforward: you place one for block inside another, but the execution order and runtime cost deserve careful attention before you use it in performance-sensitive code.

Basic Syntax of a Nested For Loop

The simplest form of a nested for loop in Java looks like this:

for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { System.out.println("i=" + i + ", j=" + j); } }

The outer loop starts with i = 0. For each value of i, the inner loop runs completely from j = 0 to j = 2. The output is nine lines: (0,0), (0,1), (0,2), (1,0), and so on. The inner loop is reinitialized each time the outer loop advances, so j starts from zero again for every i. This behavior is the core of nested loops: the inner loop's full cycle is repeated for each outer iteration.

How Execution Order Works

Understanding the execution order prevents off-by-one errors and helps you reason about algorithm behavior. The Java compiler evaluates the outer loop's condition, then enters the block. Inside that block, the inner loop initializes, checks its condition, executes its body, and updates its counter. When the inner loop's condition becomes false, control returns to the outer loop's update expression, and the outer condition is checked again.

Consider a nested loop that prints a multiplication table:

for (int row = 1; row <= 3; row++) { for (int col = 1; col <= 3; col++) { System.out.print(row * col + " "); } System.out.println(); }

Each row corresponds to one outer iteration. The inner loop prints three values, and then a newline is printed after the inner loop finishes. The outer loop does not advance until the inner loop has completed its entire sequence. This ordering is deterministic and is the basis for many matrix and grid algorithms.

Common Use Cases for Nested Loops

Nested for loops are the natural choice when you need to access every element in a two-dimensional structure. A typical example is iterating over a int[][] matrix:

int[][] matrix = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { System.out.print(matrix[i][j] + " "); } System.out.println(); }

This pattern works for any rectangular or jagged array. The outer loop iterates over rows, and the inner loop iterates over columns. Nested loops also appear in algorithms like bubble sort, insertion sort, and any code that compares each element with every other element. When the problem requires examining all pairs, a nested loop is often the most direct implementation.

Performance Implications of Nested Loops

The most important technical concern with a Java nested for loop is its time complexity. If the outer loop runs n times and the inner loop runs m times, the total number of iterations is n * m. When both loops depend on the same input size, the complexity becomes O(n²). For large inputs, this quadratic growth can make the code unusable. The actual runtime depends on the work done inside the inner loop, but even a simple operation repeated a billion times will be slow.

There is no benchmark data here, but the mechanism is clear: each inner iteration involves a condition check, a body execution, and an update expression. The Java HotSpot JIT compiler may optimize certain patterns, such as loop unrolling, but the fundamental iteration count still dominates. If you need to improve performance, first reduce the number of iterations rather than micro-optimizing the loop body.

Common Mistakes and Pitfalls

A frequent mistake is reusing the same loop variable name in both loops. Java allows variable shadowing, but it makes the code confusing and can lead to subtle bugs:

for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) { // Compiles but shadows the outer i // ... } }

This compiles because the inner i is in a separate scope, but it prevents the inner loop from accessing the outer counter. Use distinct names like i and j, or better, descriptive names like row and col. Another pitfall is an incorrect inner loop bound that depends on the outer variable. For example, when iterating a triangular region, you might intend j < i but write j <= i, producing one extra iteration. Always verify the boundary conditions with small test cases.

A more subtle issue is modifying the loop variable inside the body. Changing i or j during iteration can cause skipped iterations or infinite loops. If you need to break out of a nested loop, use a labeled break rather than trying to manipulate the counter:

outer: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (someCondition) { break outer; } } }

The label outer allows you to exit both loops at once. Without it, break only exits the inner loop, and the outer loop continues with the next iteration.

Alternatives to Nested Loops

Nested loops are not always the best tool. Java's Stream API can express certain nested iterations more concisely, especially when you need to filter, map, or collect results. For example, generating all pairs from two lists:

List<Integer> list1 = List.of(1, 2, 3); List<Integer> list2 = List.of(4, 5, 6); list1.stream() .flatMap(a -> list2.stream().map(b -> a * b)) .forEach(System.out::println);

This is functionally equivalent to a nested loop but reads more declaratively. However, streams add overhead and are not always faster. For simple array traversal, a traditional nested loop is often clearer and more efficient. Recursion is another alternative for problems like tree traversal, but it introduces call stack overhead and is not a direct replacement for grid iteration.

When to Use Nested Loops vs Other Approaches

Use a nested for loop when you need to access every combination of two indices and the logic is straightforward. It is the most readable solution for matrix operations, adjacency matrix traversal, and brute-force algorithms. If the inner loop's body is complex or if you need early termination based on a condition, a nested loop with a labeled break is often more explicit than a stream-based solution.

If the problem can be solved with a single loop using arithmetic, such as converting a 2D index to a 1D index, that is usually faster. For example, iterating over a flattened array with a single loop avoids the overhead of a second counter. But that only works when the data is stored in a flat structure. When you need to process rows and columns independently, the nested loop is the natural fit.

Another consideration is maintainability. A deeply nested loop with three or four levels becomes hard to read and test. If you find yourself nesting more than two loops, consider extracting the inner loop into a separate method or using a different algorithm. The goal is not to eliminate nested loops entirely, but to use them where they are the clearest representation of the problem.

Optimizing the Inner Loop Body

Even when a nested loop is unavoidable, you can reduce its cost by moving invariant work outside the inner loop. For example, if you compute a value that does not depend on the inner loop variable, compute it once in the outer loop:

for (int i = 0; i < n; i++) { int base = computeBase(i); // does not depend on j for (int j = 0; j < m; j++) { process(base, j); } }

This avoids recomputing computeBase(i) for every j. Similarly, if you are summing values from a 2D array, consider whether you can accumulate partial sums in the outer loop. These micro-optimizations do not change the O(n²) complexity, but they can reduce the constant factor, which matters when the input size is fixed and the loop is a known bottleneck.

Another technique is to use break or continue to skip unnecessary iterations. For example, if you are searching for a specific element, you can break out of the inner loop once found, and if you also want to stop the outer loop, use a labeled break. This can turn a worst-case O(n²) algorithm into an average-case that is much faster, depending on the data distribution.

Finally, be aware of the JVM's loop optimizations. The HotSpot JIT can perform loop unrolling and strength reduction, but these optimizations are most effective when the loop bounds are known and the body is simple. If your loop body contains method calls that cannot be inlined, the JIT may not optimize as aggressively. Profiling with a tool like JFR or a simple timing harness is the only reliable way to know where the real cost lies. Avoid guessing; measure the specific loop in your context.

java nested for loop: Practical Usage and Code Examples | RYUSLOG DEV