Print Wave Pattern in Java
Problem
A wave pattern places one star per column, with its row position cycling smoothly between the top, middle, and bottom of a fixed height as the column number increases, tracing a continuous up-and-down wave.
Given a number of columns, print a three-row wave line of stars that rises to the top row and dips to the bottom row as the columns progress.
Java Program
public class WavePattern {
public static void main(String[] args) {
int rows = 3, cols = 12;
for (int row = 0; row < rows; row++) {
StringBuilder line = new StringBuilder();
for (int col = 0; col < cols; col++) {
int cyclePos = col % 4; // position within the repeating 4-column wave cycle
int starRow = (cyclePos == 0) ? 2 : (cyclePos == 2) ? 0 : 1; // trough, midline, crest, midline
line.append(row == starRow ? '*' : ' ');
}
System.out.println(line);
}
}
}Output
Core Logic
Grouping the columns into repeating blocks of four, and mapping each position within a block to a row that dips, rises to the middle, peaks, then returns to the middle, traces one full wave crest every four columns.
- 1
col % 4gives each column's position within its 4-column cycle, a value from0to3. - 2Position
0places the star on row2(the trough), position2places it on row0(the crest), and positions1and3both place it on row1(the midline, once rising and once falling). - 3Every other row-and-column combination stays blank, keeping exactly one star per column.
0 through 3, the star moves from the trough (row 2) up through the midline (row 1) to the crest (row 0) and back down through the midline again — one complete wave — before repeating from column 4.Key Point: This uses only three rows of amplitude — using more rows without changing the 4-column cycle would leave the extra rows permanently blank, since the cycle only ever visits three distinct heights.
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 WavePatternStream {
public static void main(String[] args) {
int rows = 3, cols = 12;
IntStream.range(0, rows)
.mapToObj(row -> IntStream.range(0, cols)
.mapToObj(col -> {
int cyclePos = col % 4;
int starRow = (cyclePos == 0) ? 2 : (cyclePos == 2) ? 0 : 1;
return row == starRow ? "*" : " ";
})
.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 column cycle position, mapping every column index directly to that test reproduces the wave 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 -> ...)computes each position'scyclePosandstarRowexactly as the loop version does, then compares it against the current row. - 4
Collectors.joining("")concatenates that row's characters with no separator, matching the loop version's plain character append.
cyclePos = 1), starRow is 1, so only row 1's stream produces a "*" at that column — 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.