Print X Pattern in Java
Problem
The letter X is traced by a square grid's two diagonals at once — the main diagonal running top-left to bottom-right, and the anti-diagonal running top-right to bottom-left.
Given a grid size n, print an X shape made of stars where the two diagonals of the grid meet.
Java Program
public class XPattern {
public static void main(String[] args) {
int n = 5;
for (int row = 0; row < n; row++) {
StringBuilder line = new StringBuilder();
for (int col = 0; col < n; col++) {
if (col == row || col == n - 1 - row) { // on the main diagonal or the anti-diagonal
line.append('*');
} else {
line.append(' ');
}
}
System.out.println(line);
}
}
}Output
Core Logic
Checking whether each grid position sits on either diagonal, and printing a star only there, traces both diagonal lines in a single sweep of the grid.
- 1
col == rowtests whether a position lies on the main diagonal, running from the top-left corner down to the bottom-right. - 2
col == n - 1 - rowtests the anti-diagonal instead, running from the top-right corner down to the bottom-left. - 3A star is printed if either condition holds; every other position prints a space.
- 4Both loops run
0ton - 1, visiting every cell of the square grid exactly once.
row = 2 for n = 5), both diagonal conditions point to the same column, col = 2 — that's why the X pinches to a single star there.Key Point: The two diagonal checks only agree at the grid's exact center when n is odd — that's what makes the shape cross cleanly rather than leaving a gap in the middle.
Why: The nested loop evaluates the diagonal conditions once per grid cell, a total proportional to n², with no storage beyond the loop counters and a per-row StringBuilder.
Key Concepts
Approach 2: Java 8
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class XPatternStream {
public static void main(String[] args) {
int n = 5;
IntStream.range(0, n)
.mapToObj(row -> IntStream.range(0, n)
.mapToObj(col -> (col == row || col == n - 1 - row) ? "*" : " ")
.collect(Collectors.joining("")))
.forEach(System.out::println);
}
}
Output
Core Logic
Since each position's on-diagonal status depends only on its own row and column, mapping every column index directly to that test traces both diagonals without visiting neighboring cells.
- 1
IntStream.range(0, n)generates one stream element per row. - 2For each row, an inner
IntStream.range(0, n)generates that row's column indices. - 3
.mapToObj(col -> (col == row || col == n - 1 - row) ? "*" : " ")tests the same main-diagonal and anti-diagonal conditions the loop version does, per position. - 4
Collectors.joining("")concatenates that row's characters with no separator, matching the loop version's plain character append.
row = 2 for n = 5), both diagonal conditions point to column 2, so only that one position in the row maps to "*".Key Point: Each diagonal test reads only its own row and col, so it maps onto a stream exactly as cleanly as it fits inside a nested loop — no dependency on any other cell's result.
Why: The nested streams still evaluate the diagonal conditions once per grid cell, a total proportional to n², without collecting the full grid.