Print Zig-Zag Pattern in Java
Problem
A zigzag pattern places one star per column, with its row position cycling through a repeating up-down sequence as the column number increases, tracing a continuous zigzag line.
Given a number of columns, print a three-row zigzag line of stars that moves down, up, down, and up again as the columns progress.
Java Program
public class ZigZagPattern {
public static void main(String[] args) {
int rows = 3, cols = 9;
for (int row = 0; row < rows; row++) {
StringBuilder line = new StringBuilder();
for (int col = 0; col < cols; col++) {
// Each 4-column cycle traces one full down-up-down-up zigzag
boolean star = (row == 0 && col % 4 == 0)
|| (row == 1 && (col % 4 == 1 || col % 4 == 3))
|| (row == 2 && col % 4 == 2);
line.append(star ? '*' : ' ');
}
System.out.println(line);
}
}
}Output
Core Logic
Grouping the columns into repeating blocks of four, and assigning each position within a block to a specific row, traces one full down-up-down-up cycle of the zigzag every four columns.
- 1
col % 4gives each column's position within its 4-column cycle, a value from0to3. - 2Position
0places a star on row0(the top row), positions1and3place it on row1(the middle row), and position2places it on row2(the bottom row). - 3Every other row-and-column combination stays blank, so exactly one star is printed per column.
0 through 3, the star lands on row 0, then row 1, then row 2, then row 1 again — one full zigzag cycle — before the pattern repeats starting at column 4.Key Point: The middle row (row 1) is the only one that gets a star twice per cycle — once on the way down and once on the way back up — which is what gives the line its continuous zigzag shape instead of three disconnected rows of dots.
Why: The nested loop visits each of the fixed grid positions once to decide star or space, with no storage beyond the loop counters.
Key Concepts
Approach 2: Java 8
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ZigZagPatternStream {
public static void main(String[] args) {
int rows = 3, cols = 9;
IntStream.range(0, rows)
.mapToObj(row -> IntStream.range(0, cols)
.mapToObj(col -> {
boolean star = (row == 0 && col % 4 == 0)
|| (row == 1 && (col % 4 == 1 || col % 4 == 3))
|| (row == 2 && col % 4 == 2);
return star ? "*" : " ";
})
.collect(Collectors.joining("")))
.forEach(System.out::println);
}
}
Output
Core Logic
Since each position's star-or-blank status depends only on its own row and its column's cycle position, mapping every column index directly to that test reproduces the zigzag without any state carried between columns.
- 1
IntStream.range(0, rows)generates one stream element per row. - 2For each row, an inner
IntStream.range(0, cols)generates that row's column indices. - 3
.mapToObj(col -> ...)evaluates the exact samecol % 4cycle condition the loop version uses to decide star or space. - 4
Collectors.joining("")concatenates that row's characters with no separator, matching the loop version's plain character append.
col % 4 == 1), only row 1's condition holds, so row 1's stream produces a "*" at that column while rows 0 and 2 produce a space.Key Point: The cycle test reads only its own row and col, so it maps onto a stream exactly as cleanly as it fits inside a nested loop — the repeating cycle is just arithmetic, not carried state.
Why: The nested streams still evaluate the cycle test once per grid position, the same total work as the loop version, without collecting the full grid.