Print Hollow Triangle in Java
Problem
A hollow triangle keeps only the two slanted edges and the base filled in — every position strictly inside those edges is left blank.
Given a number of rows, print a left-aligned triangle outline of stars with a blank interior and a solid base.
Java Program
public class HollowTriangle {
public static void main(String[] args) {
int n = 6;
for (int i = 1; i <= n; i++) {
StringBuilder line = new StringBuilder();
for (int j = 1; j <= i; j++) {
if (j == 1 || j == i || i == n) { // edge position, or the solid base row
line.append("*");
} else {
line.append(" ");
}
}
System.out.println(line);
}
}
}Output
Core Logic
Printing a star only at the first position, the last position of each row, or anywhere in the final row leaves every interior position blank while still closing the shape at the bottom.
- 1The outer loop runs
ifrom 1 ton, and for each row the inner loop runsjfrom 1 toi, matching the same widening shape as a solid triangle. - 2A star is printed when
j == 1(the left edge),j == i(the right edge), ori == n(the base row). - 3Every other position — strictly inside a row that isn't the base — prints a blank space instead.
- 4Without the last-row exception, the triangle's two slanted edges would never actually meet at a closed bottom.
n = 6, rows 1 through 5 show only their two edge stars, while row 6 — the base — prints all six stars solid.Key Point: The base row needs its own special case because j == 1 and j == i alone would leave every row's middle hollow — including the bottom, which would make the shape look open instead of enclosed.
Why: The nested loop still visits every position within the triangular grid to decide star-vs-space, a total proportional to n², with only the loop counters kept.
Key Concepts
Approach 2: Java 8
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class HollowTriangleStream {
public static void main(String[] args) {
int n = 6;
IntStream.rangeClosed(1, n)
.mapToObj(i -> IntStream.rangeClosed(1, i)
.mapToObj(j -> (j == 1 || j == i || i == n) ? "*" : " ")
.collect(Collectors.joining("")))
.forEach(System.out::println);
}
}
Output
Core Logic
Since each position's edge-or-base status depends only on its own row and column, mapping every column index directly to that test reproduces the outline without visiting neighboring cells.
- 1
IntStream.rangeClosed(1, n)generates one stream element per rowi. - 2For each row, an inner
IntStream.rangeClosed(1, i)generates that row's column positionsj, matching the triangle's widening shape. - 3
.mapToObj(j -> (j == 1 || j == i || i == n) ? "*" : " ")tests the same edge-or-base condition 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.
j = 1 and j = 4 satisfy the edge test, so the inner stream produces a star, two blanks, and a closing star: * *.Key Point: The base-row exception is still just one more clause in the same per-cell formula — it maps onto a stream exactly as cleanly as it fits inside a nested loop, with no dependency on any other cell's result.
Why: The nested streams still evaluate the edge-or-base condition once per position within the triangular grid, the same total work as the loop version.