Numbers, Triangles, and Tables: Mastering Loops (Java)
Here’s something most programming courses don’t tell you upfront: you’ll spend the rest of your career writing loops. For loops, while loops, iterator patterns—they’re fundamental to everything you’ll build. This lab isn’t about making something flashy. It’s about getting so comfortable with loops that you stop thinking about the syntax and start thinking about the patterns.
Why This Lab Matters
This feels like a simple assignment. Generate some numbers, print some asterisks, make a multiplication table. And yeah, on the surface, that’s what it is. But here’s what you’re actually learning:
Loop Mechanics: You need to understand exactly when a loop starts, when it ends, what happens in each iteration, and how variables change over time. Get this wrong and you’ll debug off-by-one errors for the rest of your life.
String Building: Real programs generate output constantly—reports, logs, formatted data, user interfaces. This lab teaches you how to build strings programmatically, which is something you’ll do daily in any development job.
Pattern Recognition: Triangles and tables aren’t random exercises. They’re patterns. Learning to see the pattern behind the visual output is the skill that lets you translate business requirements into code.
Return vs. Print: One of the most important lessons here is understanding the difference between returning a value and printing it. Methods that return values are composable, testable, and reusable. Methods that print directly are none of those things.
The Three Challenges
You’re building three utility classes. Each one focuses on a different aspect of loop mastery:
1. NumberUtilities: Counting and Generating
Generate sequences of numbers based on different rules:
- Even numbers in a range
- Odd numbers in a range
- Squared values with a step
- Custom ranges with step values
- Exponentiation patterns
What you’re learning: Basic loop iteration, conditional logic inside loops, and numeric operations.
2. TriangleUtilities: Pattern Building
Generate ASCII triangle patterns of different sizes:
- Single rows of asterisks
- Small triangles (4 rows)
- Large triangles (10 rows)
- Custom-sized triangles
What you’re learning: Nested loops, visual pattern generation, and building output incrementally.
3. TableUtilities: Formatted Output
Create multiplication tables with proper formatting:
- Small tables (4×4)
- Large tables (10×10)
- Custom-sized tables with alignment
What you’re learning: Nested loops with dual variables, string formatting, and producing structured output.
The Critical Concept: Return vs. Print
Let me emphasize this because it trips up every beginner:
Your methods must RETURN strings, not print them.
Here’s why this matters:
1// Bad: Prints directly
2public static void getEvenNumbersBad(int start, int stop) {
3 for (int i = start; i < stop; i++) {
4 if (i % 2 == 0) {
5 System.out.println(i); // Can't test this, can't compose it
6 }
7 }
8}
9
10// Good: Returns a string
11public static String getEvenNumbers(int start, int stop) {
12 String result = "";
13 for (int i = start; i < stop; i++) {
14 if (i % 2 == 0) {
15 result += i;
16 }
17 }
18 return result; // Testable, composable, reusable
19}When you return values, your code becomes a building block. When you print directly, you’ve created a dead end.
Phase-by-Phase Implementation
Phase 1: Simple Number Sequences
Start with the easiest challenge: generating even and odd numbers.
1public static String getEvenNumbers(int start, int stop) {
2 // Generate even numbers from start to stop (exclusive)
3 String result = "";
4 for (int num = start; num < stop; num++) {
5 if (num % 2 == 0) {
6 result += num;
7 }
8 }
9 return result;
10}Think about:
- What does a
forloop withnum < stopactually give you? - How do you check if a number is even? (Hint: modulo operator)
- Do we need to convert the number to a string before concatenating?
- What happens if there are no even numbers in the range?
Test it:
1System.out.println(getEvenNumbers(5, 20)); // Should output: "681012141618"
2System.out.println(getEvenNumbers(10, 15)); // Should output: "1012"Common mistakes:
- Including
stopin the loop (remember, we use<not<=) - Testing for odd when you meant even (or vice versa)
- Forgetting that Java concatenation auto-converts primitives to strings
Phase 2: Loops with Steps and Operations
Now level up with squares and exponentiation:
1public static String getSquareNumbers(int start, int stop, int step) {
2 // Generate squared values with a step
3 String result = "";
4 for (int num = start; num < stop; num += step) {
5 int squared = num * num;
6 result += squared;
7 }
8 return result;
9}Think about:
- How does the
stepparameter change the iteration? - When do you calculate the square—before or after the loop check?
- Could you use
Math.pow(num, 2)instead ofnum * num? (Yes, but multiplication is faster)
Test it:
1System.out.println(getSquareNumbers(5, 15, 5)); // Should output: "25100"
2System.out.println(getSquareNumbers(1, 5, 1)); // Should output: "1491625"Phase 3: Building Visual Patterns
Triangles are where loops get interesting. You need to think in two dimensions:
1public static String getRow(int width) {
2 // Generate a row of asterisks
3 String result = "";
4 for (int i = 0; i < width; i++) {
5 result += "*";
6 }
7 return result;
8}
9
10public static String getTriangle(int n) {
11 // Generate an n-row triangle
12 String result = "";
13 for (int i = 1; i <= n; i++) {
14 result += getRow(i) + "\n";
15 }
16 return result;
17}Think about:
- Why does the loop use
i <= ninstead ofi < n? - What’s the pattern between row number and number of asterisks?
- Why add
\nafter each row? - How does
getRow()makegetTriangle()simpler?
Test it:
1System.out.println(getTriangle(4));
2// Should output:
3// *
4// **
5// ***
6// ****This is decomposition in action. You break the problem into smaller pieces.
getRow() handles one row. getTriangle() handles multiple rows by calling
getRow() repeatedly. This is how you manage complexity.
Phase 4: Nested Loops and Formatting
Multiplication tables require nested loops—a loop inside a loop:
1public static String getMultiplicationTable(int width) {
2 // Generate a multiplication table of given width
3 String result = "";
4 for (int row = 1; row <= width; row++) {
5 for (int col = 1; col <= width; col++) {
6 int product = row * col;
7 result += String.format("%3d |", product);
8 }
9 result += "\n";
10 }
11 return result;
12}Think about:
- Why do you need TWO loops?
- What does the outer loop represent? The inner loop?
- What’s happening with
String.format("%3d |", product)? (Right-align in 3 characters) - Why add the newline after the inner loop finishes?
Test it:
1System.out.println(getMultiplicationTable(3));
2// Should output:
3// 1 | 2 | 3 |
4// 2 | 4 | 6 |
5// 3 | 6 | 9 |The nested loop pattern:
- Outer loop: controls rows
- Inner loop: controls columns
- After inner loop completes: move to next row
This pattern shows up everywhere: processing 2D arrays, generating grids, working with matrices, building tables in web apps.
String Building Strategies
You have multiple ways to build strings in Java. Know them all:
String Concatenation (Simple but works)
1String result = "";
2for (int i = 0; i < 5; i++) {
3 result += i;
4}Pros: Easy to understand, works everywhere
Cons: Can be slow for very large strings (creates new string each time)
StringBuilder (Efficient and recommended)
1StringBuilder sb = new StringBuilder();
2for (int i = 0; i < 5; i++) {
3 sb.append(i);
4}
5String result = sb.toString();Pros: Much faster for large strings, mutable
Cons: Slightly more verbose
String.format (For formatting)
1String result = "";
2for (int i = 0; i < 5; i++) {
3 result += String.format("%d", i);
4}Pros: Clean syntax, supports formatting
Cons: Overhead for simple concatenation
For this lab, simple concatenation works fine for small strings. For production
code with large strings or many concatenations, use StringBuilder. The important
thing is understanding what you’re doing, not memorizing “the right way.”
Common Pitfalls and How to Avoid Them
1. Off-by-One Errors
The classic loop mistake. Remember: for (int i = start; i < stop; i++) goes UP TO but NOT
INCLUDING stop.
1// Wrong: Misses the last number you want
2for (int i = 1; i < 10; i++) { // Goes 1-9, not 1-10
3 System.out.println(i);
4}
5
6// Right: Use <= if you want to include the end
7for (int i = 1; i <= 10; i++) { // Goes 1-10
8 System.out.println(i);
9}2. String Concatenation Performance
For loops with many iterations, string concatenation can be slow:
1// Works but slow for large loops
2String result = "";
3for (int i = 0; i < 10000; i++) {
4 result += i; // Creates new string each time
5}
6
7// Better for large loops
8StringBuilder sb = new StringBuilder();
9for (int i = 0; i < 10000; i++) {
10 sb.append(i); // Modifies existing buffer
11}
12String result = sb.toString();3. Printing Instead of Returning
Your tests will fail if you print instead of return:
1// Wrong: Tests can't capture this
2public static void getEvenNumbers(int start, int stop) {
3 for (int i = start; i < stop; i++) {
4 if (i % 2 == 0) {
5 System.out.println(i); // Goes to console, not to caller
6 }
7 }
8}
9
10// Right: Tests can verify this
11public static String getEvenNumbers(int start, int stop) {
12 String result = "";
13 for (int i = start; i < stop; i++) {
14 if (i % 2 == 0) {
15 result += i;
16 }
17 }
18 return result; // Caller receives the value
19}4. Not Understanding Nested Loops
Each iteration of the outer loop runs the ENTIRE inner loop:
1for (int row = 0; row < 3; row++) { // Runs 3 times
2 for (int col = 0; col < 4; col++) { // Runs 4 times PER row
3 System.out.println(row + ", " + col);
4 }
5}
6// Total iterations: 3 × 4 = 12If you’re confused about what’s happening, add print statements to see the iteration count:
1int iteration = 0;
2for (int row = 0; row < 3; row++) {
3 for (int col = 0; col < 4; col++) {
4 iteration++;
5 System.out.printf("Iteration %d: row=%d, col=%d%n", iteration, row, col);
6 }
7}Testing Your Code
The repository comes with tests. Here’s how to use them effectively:
Java:
1cd java
2mvn testReading test failures:
When a test fails, it tells you:
- What method was being tested
- What input was provided
- What output was expected
- What output you actually returned
Use this information to debug. Don’t just stare at your code—look at what the test expected and compare it to what you returned.
Testing incrementally:
Don’t write all three utilities and then test. Write one method, test it, make it pass, then move to the next. This is how professionals work.
The Bigger Picture
These exercises might seem basic, but they’re teaching you patterns you’ll use constantly:
Number generation → Database queries returning ranges, pagination, batch processing
Triangle patterns → Understanding nested structures, building UI components, generating reports
Multiplication tables → Formatting data for display, building grids, creating structured output
Six months from now, you won’t be making multiplication tables. But you WILL be using nested loops to process data, format output, and generate structured information. The patterns are the same.
Going Further (Optional Extensions)
Once you’ve got the basics working, try these challenges:
Advanced Number Utilities
- Generate prime numbers in a range
- Create Fibonacci sequences
- Generate numbers in different bases (binary, hexadecimal)
Pattern Variations
- Right-aligned triangles
- Inverted triangles
- Diamond patterns
- Hollow triangles (asterisks only on edges)
Table Enhancements
- Addition tables, not just multiplication
- Custom operators (subtraction, division)
- Formatted decimal alignment
- Color-coded output (for terminal apps)
The Professional Mindset
Here’s what this lab is really teaching:
Precision: Loops require exact thinking. Off by one? Wrong output. Wrong condition? Infinite loop. This precision is what separates working code from broken code.
Decomposition: Breaking problems into smaller methods. getRow() helps
build getTriangle(). This is how you manage complexity in real systems.
Testability: Returning values instead of printing makes your code testable. Tests are how professional teams ensure code quality.
Pattern Recognition: Seeing the loop pattern behind the visual output. This skill translates directly to translating business requirements into code.
Getting Started
Here’s your action plan:
- Clone the repository from https://github.com/ZCW-Summer25/NumbersTrianglesTables
- Choose your language (Java or Python) and navigate to that directory
- Start with NumberUtilities: Get comfortable with basic loops
- Move to TriangleUtilities: Practice nested loops and patterns
- Finish with TableUtilities: Master two-dimensional iteration
- Run tests frequently: Don’t wait until everything is done
- Experiment: Once tests pass, try the optional extensions
Remember: Loops Are Forever
You will write loops in every program you create for the rest of your career. Getting comfortable with them now—understanding how they work, what patterns they follow, and how to debug them—is time incredibly well spent.
Don’t rush through this. Take the time to understand each loop’s behavior. Print intermediate values. Draw diagrams of what’s happening. The investment you make now in truly understanding loops will pay off every single day you write code.