Java loops practice — text, statistics and patterns
There’s an FP2 lab exercise that looks trivial until you actually try it: counting how many vowels a sentence has. In Python it was for c in text: and that’s it, no further thought needed. In Java, the first thing you instinctively write is for (char c : text), and the compiler throws back an error you don’t understand: “for-each not applicable to type ‘String'”. That’s when you remember that in Java a String can’t be looped over directly, and you need to find a way around it.
That workaround is exactly the subject of this practice. We’re going to loop through text character by character in both ways Java allows, and along the way you’ll write three programs that combine everything you already know about loops: for, for-each, while and do-while.
Open VS Code, create a folder loops_practice and one .java file per program.
Table of Contents
Java loops practice — Program 1: Text analyser
This program counts letters, vowels, digits and spaces in a sentence, using both ways of looping through a String in Java. It’s the exercise that answers the question from the start.
import java.util.Scanner;
public class TextAnalyser {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== TEXT ANALYSER ===");
System.out.println();
System.out.print("Type a sentence: ");
String sentence = sc.nextLine();
// FORM 1 — with charAt() and an indexed for
// The most direct choice when you also need to know the position
int letters = 0, digits = 0, spaces = 0, uppercase = 0;
for (int i = 0; i < sentence.length(); i++) {
char c = sentence.charAt(i); // the character at position i
if (Character.isLetter(c)) {
letters++;
if (Character.isUpperCase(c)) {
uppercase++;
}
} else if (Character.isDigit(c)) {
digits++;
} else if (c == ' ') {
spaces++;
}
}
// FORM 2 — with toCharArray() and for-each
// Cleaner when you only care about the character, not its position
int vowels = 0;
for (char c : sentence.toLowerCase().toCharArray()) {
// toLowerCase() before comparing — so "A" and "a" both count
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
vowels++;
}
}
System.out.println();
System.out.println("--- Result ---");
System.out.printf("Total length: %d characters%n", sentence.length());
System.out.printf("Letters: %d%n", letters);
System.out.printf(" Of which, uppercase: %d%n", uppercase);
System.out.printf("Vowels: %d%n", vowels);
System.out.printf("Digits: %d%n", digits);
System.out.printf("Spaces: %d%n", spaces);
// Palindrome — check by reading from both ends at once
String clean = sentence.toLowerCase().replace(" ", "");
boolean isPalindrome = true;
for (int i = 0; i < clean.length() / 2; i++) {
// Compare the i-th character from the start with the i-th from the end
if (clean.charAt(i) != clean.charAt(clean.length() - 1 - i)) {
isPalindrome = false;
break; // once we find a mismatch, no need to keep checking
}
}
System.out.printf("Is it a palindrome? %s%n", isPalindrome ? "Yes" : "No");
sc.close();
}
}
Try it with “Anita lava la tina” (a classic Spanish palindrome) and confirm the program detects it correctly. The loop that checks it only goes halfway through the text (clean.length() / 2), because comparing the second half would just repeat the same work twice — if the first character matches the last, and the second matches the second-to-last, you already know the rest will match too if you keep following that pattern.
for (char c : sentence) doesn’t compile. A String isn’t directly loopable with for-each in Java — only arrays and collections are. To use for-each with text, you first need to convert it with sentence.toCharArray(), which does return an array of char.
Java loops practice — Program 2: Grade array statistics
Here you work with loops over numbers instead of text: minimum, maximum, average and how far each value strays from the average, all using for-each and indexed for depending on what you need at each moment.
import java.util.Scanner;
public class GradeStatistics {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== GRADE STATISTICS ===");
System.out.println();
System.out.print("How many grades will you enter? ");
int n = sc.nextInt();
double[] grades = new double[n];
for (int i = 0; i < n; i++) {
System.out.printf("Grade %d: ", i + 1);
grades[i] = sc.nextDouble();
}
// Sum with for-each — we don't need the index, just the value
double sum = 0;
for (double grade : grades) {
sum += grade;
}
double average = sum / n;
// Minimum and maximum — start by assuming the first one is both
// and adjust as we compare it against the rest
double minimum = grades[0];
double maximum = grades[0];
for (double grade : grades) {
if (grade < minimum) minimum = grade;
if (grade > maximum) maximum = grade;
}
// How many grades are more than 1 point away from the average
// Here we do need the index, so we can say which one it is
int outliers = 0;
for (int i = 0; i < n; i++) {
double distance = Math.abs(grades[i] - average);
if (distance > 1.0) {
outliers++;
}
}
System.out.println();
System.out.println("--- Result ---");
System.out.printf("Average: %.2f%n", average);
System.out.printf("Minimum: %.2f%n", minimum);
System.out.printf("Maximum: %.2f%n", maximum);
System.out.printf("Range: %.2f%n", maximum - minimum);
System.out.printf("Grades more than 1 point from average: %d of %d%n", outliers, n);
sc.close();
}
}
Notice the pattern for minimum and maximum: no special trick is needed, just assume the first element is both the minimum and the maximum, and correct that assumption as you look at the rest of the array. It’s the same pattern you’ll use to find the most expensive or cheapest item in any list of data.
When you need the value of each element and nothing else, use for-each. When you need to know the position of something, or compare one position with another (like with the palindrome in the previous program), you need the index, so use a counted for.
Java loops practice — Program 3: Pattern generator with validation
The classic nested-loop exercise, with a twist: the user picks which pattern to see from a menu, so you combine do-while, switch and nested for loops in the same program.
import java.util.Scanner;
public class PatternGenerator {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== PATTERN GENERATOR ===");
int option;
do {
System.out.println("\n1. Triangle");
System.out.println("2. Pyramid");
System.out.println("3. Chessboard");
System.out.println("4. Quit");
System.out.print("Option: ");
option = sc.nextInt();
if (option == 4) {
System.out.println("Goodbye");
break; // exit the do-while without asking for a size
}
System.out.print("Size (3-10): ");
int n = sc.nextInt();
// Validate before drawing anything — if the size is wrong, warn and go back to the menu
if (n < 3 || n > 10) {
System.out.println("✗ Size must be between 3 and 10");
continue; // jumps straight to the next pass of the do-while
}
System.out.println();
switch (option) {
case 1:
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
System.out.print("* ");
}
System.out.println();
}
break;
case 2:
for (int i = 1; i <= n; i++) {
// Spaces to centre it — shrink as the asterisks grow
for (int s = 0; s < n - i; s++) {
System.out.print(" ");
}
for (int j = 0; j < i; j++) {
System.out.print("* ");
}
System.out.println();
}
break;
case 3:
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// (i + j) even for one square, odd for the other — the chessboard trick
if ((i + j) % 2 == 0) {
System.out.print("■ ");
} else {
System.out.print("□ ");
}
}
System.out.println();
}
break;
default:
System.out.println("✗ Invalid option");
}
} while (true);
sc.close();
}
}
The (i + j) % 2 == 0 in the chessboard is the trickiest pattern to spot at first glance, so it deserves its own explanation. When the sum of the row and column is even, we draw one square; when it’s odd, we draw the other. Since row and column change together across the two nested loops, that even-odd result alternates on its own, square by square, and the chessboard pattern appears without having to keep track of anything else.
continue jumps straight to the next pass of the loop without running the rest of that pass’s code. In this program, if the size isn’t valid, continue sends you back to the top of the do-while — that is, back to the menu — without trying to draw anything with a wrong size.
Visualise with Python Tutor
Select Java from the dropdown and paste in pythontutor.com:
public class Demo {
public static void main(String[] args) {
String text = "ana";
// Palindrome check, step by step
boolean isPalindrome = true;
for (int i = 0; i < text.length() / 2; i++) {
char fromStart = text.charAt(i);
char fromEnd = text.charAt(text.length() - 1 - i);
if (fromStart != fromEnd) {
isPalindrome = false;
break;
}
}
System.out.println("Palindrome? " + isPalindrome);
// Minimum and maximum of an array
int[] numbers = {7, 2, 9, 4};
int minimum = numbers[0];
int maximum = numbers[0];
for (int n : numbers) {
if (n < minimum) minimum = n;
if (n > maximum) maximum = n;
}
System.out.println("Minimum: " + minimum + ", Maximum: " + maximum);
}
}
Step through and watch two things. In the palindrome check, notice how text.length() / 2 limits the loop to a single pass with “ana” (length 3, so it only compares position 0 with position 2, and the middle position doesn’t need to be compared with itself). And in the minimum and maximum, watch how minimum and maximum both start at the same value, 7, and gradually diverge as the for-each goes through 2, 9 and 4 one after another.
Summary and next step
In this practice you looped through text both ways Java allows, calculated statistics over an array by combining for and for-each depending on what you needed at each point, and controlled a full menu with do-while, switch, break and continue working together. The next time the compiler tells you that you can’t for-each over a String, you’ll know exactly what’s missing.
In the next article you’ll find exercises to solve on your own.
Next step: exercisesLoops in Java — exercises with vowels, grades and the loop that once froze on me