FizzBuzz is a counting program that prints the numbers from 1 to n, but prints Fizz for multiples of 3, Buzz for multiples of 5 and FizzBuzz for numbers divisible by both. In Java, we solve it with a loop over the numbers and an if-else chain that tests the remainder operator %, with the checks in the right order.
Interviewers use FizzBuzz as a first screening exercise, because a correct answer needs a loop with conditions and some care with edge cases. Developers also use it as a short kata to practice streams, switch expressions and unit tests.
The following example calls the fizzBuzz() method from section 2 for single numbers.
String seven = fizzBuzz(7); // "7"
String nine = fizzBuzz(9); // "Fizz"
String ten = fizzBuzz(10); // "Buzz"
String thirty = fizzBuzz(30); // "FizzBuzz"
The method returns the text for one number instead of printing it, so we can test it and reuse it in a loop or a stream. We start with the rules and the order of the checks, write the loop and stream versions, and finish with a switch expression, a version without %, custom divisors and a comparison of all solutions.
1. FizzBuzz Rules and Why the Order of Checks Matters
The program walks through the numbers 1, 2, 3 and so on up to n. For each number, it prints a word when one of the rules matches and prints the number itself when none matches. The number 15 matches all three rules, because it is a multiple of 3, a multiple of 5 and a multiple of both.

An if-else chain stops at the first condition that is true. If the chain tests 3 first, the number 15 gets the word Fizz, and the FizzBuzz branch is never reached.
static String wrongOrder(int n) {
if (n % 3 == 0) {
return "Fizz";
} else if (n % 5 == 0) {
return "Buzz";
} else if (n % 15 == 0) {
return "FizzBuzz"; // never reached
}
return String.valueOf(n);
}
String fifteen = wrongOrder(15); // "Fizz", should be "FizzBuzz"
The combined case goes first, and every later branch handles a number that we already know is not divisible by both. Because 3 and 5 have no common factor, a number divisible by both is divisible by 15, so n % 15 == 0 is the short form of n % 3 == 0 && n % 5 == 0.
2. FizzBuzz Program in Java with a for Loop
The classic solution splits the job into two methods. The fizzBuzz() method decides the text for one number, and fizzBuzzUpTo() runs a for loop from 1 to n and collects the results in a list. All the versions in this article are in the Core-Java repository with a Java 25 demo.
static String fizzBuzz(int n) {
if (n % 15 == 0) {
return "FizzBuzz";
} else if (n % 3 == 0) {
return "Fizz";
} else if (n % 5 == 0) {
return "Buzz";
}
return String.valueOf(n);
}
static List<String> fizzBuzzUpTo(int n) {
List<String> result = new ArrayList<>();
for (int i = 1; i <= n; i++) {
result.add(fizzBuzz(i));
}
return result;
}
List<String> first15 = fizzBuzzUpTo(15); // [1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz]
List<String> none = fizzBuzzUpTo(0); // []
When the task says to print the numbers, we print the list with first15.forEach(System.out::println), which gives one line per number. Returning a list keeps the logic testable, because a test can compare the list with the expected values instead of reading the console.
1
2
Fizz
4
Buzz
...
14
FizzBuzz
2.1. Building the Word Instead of Testing 15
A second loop version never tests 15. It appends Fizz when the number is divisible by 3 and Buzz when it is divisible by 5, so a multiple of 15 gets both words in the right order. If nothing was appended, the method returns the number.
static String fizzBuzzConcat(int n) {
StringBuilder word = new StringBuilder();
if (n % 3 == 0) {
word.append("Fizz");
}
if (n % 5 == 0) {
word.append("Buzz");
}
return word.isEmpty() ? String.valueOf(n) : word.toString();
}
String both = fizzBuzzConcat(45); // "FizzBuzz"
String plain = fizzBuzzConcat(8); // "8"
Interviewers often ask for a third rule after the first answer, such as Bazz for multiples of 7. The concatenation version needs one more if block, whereas the if-else chain would need new combined checks for 21, 35 and 105.
3. FizzBuzz with Java Streams
With streams, IntStream.rangeClosed(1, n) produces the numbers from 1 to n, and mapToObj() turns each number into its text. We reuse the fizzBuzz() method from section 2, so the stream contains no rules of its own.
List<String> words = IntStream.rangeClosed(1, 15).mapToObj(i -> fizzBuzz(i)).toList(); // [1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz]
String line = IntStream.rangeClosed(1, 5).mapToObj(i -> fizzBuzz(i)).collect(Collectors.joining(" ")); // "1 2 Fizz 4 Buzz"
Many online answers put the rules into a nested ternary operator inside mapToObj(). That version works when every branch returns a String, but a nested ternary is hard to read in a review, so we prefer a named method.
List<String> ternary = IntStream.rangeClosed(9, 10).mapToObj(i -> i % 15 == 0 ? "FizzBuzz" : i % 3 == 0 ? "Fizz" : i % 5 == 0 ? "Buzz" : String.valueOf(i)).toList(); // [Fizz, Buzz]
The stream version has one more advantage. The method rangeClosed() stops correctly at Integer.MAX_VALUE, whereas a loop written as for (int i = 1; i <= n; i++) never ends when n is Integer.MAX_VALUE, because i++ overflows to Integer.MIN_VALUE and the condition stays true.
4. FizzBuzz with a switch Expression
Since Java 14, a switch expression can return a value and list several labels in one case. The remainder of a division by 15 decides the word, because the pattern of Fizz and Buzz repeats every 15 numbers.
static String fizzBuzzSwitch(int n) {
return switch (Math.floorMod(n, 15)) {
case 0 -> "FizzBuzz";
case 3, 6, 9, 12 -> "Fizz";
case 5, 10 -> "Buzz";
default -> String.valueOf(n);
};
}
String switch12 = fizzBuzzSwitch(12); // "Fizz"
String negative = fizzBuzzSwitch(-3); // "Fizz"
int remainder = -3 % 15; // -3
int floorMod = Math.floorMod(-3, 15); // 12
We use Math.floorMod() instead of % on purpose. In Java, the result of % has the sign of the dividend, so -3 % 15 is -3, which matches no case label, and the method would return “-3”. The if-else version from section 2 has no such problem, because -3 % 3 is 0.
5. FizzBuzz Without the Modulo Operator
A common follow-up question asks for FizzBuzz without %. We keep two counters that count up to 3 and 5 and reset them when they reach their limit, so the loop never divides.
static List<String> fizzBuzzCounters(int n) {
List<String> result = new ArrayList<>();
int three = 0;
int five = 0;
for (int i = 1; i <= n; i++) {
three++;
five++;
String word = "";
if (three == 3) {
word += "Fizz";
three = 0;
}
if (five == 5) {
word += "Buzz";
five = 0;
}
result.add(word.isEmpty() ? String.valueOf(i) : word);
}
return result;
}
List<String> counted = fizzBuzzCounters(15); // [1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz]
The counter version shows that we understand what % computes. In production code, the remainder version is clearer, and a division costs nothing worth measuring at this scale.
6. Custom Divisors and Batch Jobs
Some versions of the game use other divisors, such as 5 and 7, or add a third word. Instead of hard-coding the rules, we pass them as a list of records and apply them in order, which is the concatenation idea from section 2 with data instead of code.
record Rule(int divisor, String word) {}
static String applyRules(int n, List<Rule> rules) {
StringBuilder text = new StringBuilder();
for (Rule rule : rules) {
if (n % rule.divisor() == 0) {
text.append(rule.word());
}
}
return text.isEmpty() ? String.valueOf(n) : text.toString();
}
List<Rule> classic = List.of(new Rule(3, "Fizz"), new Rule(5, "Buzz"));
List<Rule> fiveSeven = List.of(new Rule(5, "Fizz"), new Rule(7, "Buzz"));
String c15 = applyRules(15, classic); // "FizzBuzz"
String s35 = applyRules(35, fiveSeven); // "FizzBuzz"
String s14 = applyRules(14, fiveSeven); // "Buzz"
The same structure appears in real batch jobs. Say a CSV import reads 10,000 rows, logs progress every 100 rows and commits the database transaction every 500 rows. Row 1,500 needs both actions, so the job must check each interval on its own and must not stop at the first match, which is the FizzBuzz order problem in production code.
List<Rule> batch = List.of(new Rule(100, "log "), new Rule(500, "commit "));
String row300 = applyRules(300, batch); // "log "
String row1500 = applyRules(1500, batch); // "log commit "
7. Which FizzBuzz Version to Use
All solutions take O(n) time, because they handle each number once with a fixed number of checks. A version that returns a list needs O(n) memory for the list, whereas a version that prints each line needs O(1) extra memory. The differences are in readability and in how well each version handles changes and edge cases.
| Approach | Main idea | Negative numbers | Adding a third rule | Good for |
|---|---|---|---|---|
| if-else chain | Test 15, 3, 5 in that order | Correct | New combined checks | The expected interview answer |
| Concatenation | Append Fizz, append Buzz | Correct | One more if | Follow-up questions |
| IntStream | rangeClosed() plus mapToObj() | Correct | Depends on the mapper | Collecting results, Java 8+ style |
| switch on n % 15 | Map the remainder to a word | Needs Math.floorMod() | Rewrite the cases | Showing switch expressions |
| Counters | No division at all | Not supported | One more counter | The no-modulo question |
| Rule list | Rules as data | Correct | One more Rule | Configurable labels and jobs |
Before we hand in any version, we check a few edge cases. A limit below 1 returns an empty list, 15 and its multiples return FizzBuzz, and the words use the capitalization that the task asks for, because a test that expects FizzBuzz fails on fizzbuzz.
8. FizzBuzz FAQs
Candidates ask these questions while they prepare FizzBuzz for an interview.
8.1. Why do interviewers ask FizzBuzz?
FizzBuzz filters out candidates who cannot write a loop with conditions under light pressure. The game became widely known as a screening test in 2007, when Jeff Atwood wrote about it in Why Can’t Programmers.. Program?. Interviewers also watch how we name things, whether we test 15 first and whether we return values instead of printing.
8.2. Why do we check divisibility by 15 first?
Because an if-else chain stops at the first match. A multiple of 15 is also a multiple of 3, so a chain that tests 3 first returns Fizz and never reaches FizzBuzz. The concatenation version from section 2 avoids the question, because it applies both rules to every number.
8.3. What is the time complexity of FizzBuzz?
O(n) for n numbers, because each number needs at most three remainder checks. Some sites list O(1), which is the cost for one number, not for the whole program.
8.4. Can FizzBuzz use 5 and 7 instead of 3 and 5?
Yes. Some versions of the game use other divisors, such as 5 and 7. With the Rule list from section 6, changing the divisors is a change of data, not of code.
9. Conclusion
FizzBuzz prints Fizz for multiples of 3, Buzz for multiples of 5 and FizzBuzz for multiples of both. The only trap is the order of the checks, so we either test 15 first or build the word from both rules.
A method that returns the text for one number keeps the solution testable, and the loop, stream and switch versions all reuse it. For follow-up questions, the concatenation and rule-list versions add new words without new combined checks, and Math.floorMod() keeps the switch version correct for negative numbers.
10. References
- IntStream.rangeClosed() (Java SE 25 API)
- Math.floorMod() (Java SE 25 API)
- JLS 25, Remainder Operator %
- Fizz buzz (Wikipedia)
Happy Learning !!
private static void fizzBuzz(int i) {
int printer = 0;
for (int j = 0; printer 0)&&(j % 3 == 0) && (j % 5 == 0)) {
System.out.println(“fizz buzz”);
printer++;
} else if ((j>0)&&(j % 3 == 0)) {
System.out.println(“fizz”);
printer++;
} else if ((j>0)&&(j % 5 == 0)) {
System.out.println(“buzz”);
printer++;
} else {
System.out.println(j);
}
}
System.out.println(“TRADITION”);
}
List strRet = new ArrayList();
for (int i=1; i<=n; i++)
{
if (i%15==0)
strRet.add("FizzBuzz");
// number divisible by 5, print 'Buzz'
// in place of the number
else if (i%5==0)
strRet.add("Buzz");
// number divisible by 3, print 'Fizz'
// in place of the number
else if (i%3==0)
strRet.add("Fizz");
// number divisible by 15(divisible by
// both 3 & 5), print 'FizzBuzz' in
// place of the number
else // print the numbers
strRet.add(Integer.toString(i));
}
return strRet; //return type "List”.
}
String out;
If( n % 5 == 0 )
out = “fizz”;
if( n % 7 == 0 )
out += “buzz”;
System.out.printl( out );
Yet Another Way ;)
package com.foo; import org.junit.Assert; import org.junit.Test; public class FizzBuzz { private static final String FIZZ = "Fizz"; private static final String BUZZ = "Buzz"; @Test public void test() { loopFizzBuzz(1000000); Assert.assertEquals(fizzBuzz(8), "8"); Assert.assertEquals(fizzBuzz(27), FIZZ); Assert.assertEquals(fizzBuzz(80), BUZZ); Assert.assertEquals(fizzBuzz(90), FIZZ + BUZZ); } private void loopFizzBuzz(int size) { StringBuilder buffer = new StringBuilder(); final String lineSeparator = System.lineSeparator(); for (int i = 1; i <= size; i++) { String chunk = fizzBuzz(i) + lineSeparator; buffer.append(chunk); } System.out.println(buffer.toString()); } private String fizzBuzz(int i) { String label = String.valueOf(i); boolean isFizz = i % 3 == 0; boolean isBuzz = i % 5 == 0; if (isFizz || isBuzz) { label = ""; } if (isFizz) { label += FIZZ; } if (isBuzz) { label += BUZZ; } return label; } }Thanks for sharing. Much appreciated.
Do you think that the Java8 code example is more readable then the another one?
It is indeed shortest, but not readable as the before java8 example, IMO.