Write a program which return "fizz" if number is multiplier of 3, return "buzz" if its multiplier of 5 and return "fizzbuzz" if number is divisible by both 3 and 5. If number is not divisible by either 3 or 5 then it should just return the number itself
Here are some implementations and test codes:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.clarenceho.test; | |
public class FizzBuzz { | |
public static String doFizzBuzz1(int i) { | |
StringBuilder sb = new StringBuilder(); | |
if (i % 3 == 0) { | |
sb.append("Fizz"); | |
} | |
if (i % 5 == 0) { | |
sb.append("Buzz"); | |
} | |
if (sb.length() == 0) { | |
sb.append(i); | |
} | |
return sb.toString(); | |
} | |
public static String doFizzBuzz2(int i) { | |
if (i % 3 == 0) { | |
if (i % 5 == 0) { | |
return "FizzBuzz"; | |
} else { | |
return "Fizz"; | |
} | |
} else if (i % 5 == 0) { | |
return "Buzz"; | |
} | |
return String.valueOf(i); | |
} | |
public static String doFizzBuzz3(int i) { | |
if (i % 15 == 0) { | |
return "FizzBuzz"; | |
} else if (i % 5 == 0) { | |
return "Buzz"; | |
} else if (i % 3 == 0) { | |
return "Fizz"; | |
} | |
return String.valueOf(i); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.clarenceho.test; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.function.Function; | |
import java.util.stream.IntStream; | |
import static org.junit.Assert.*; | |
import org.junit.Before; | |
import org.junit.FixMethodOrder; | |
import org.junit.Test; | |
import org.junit.runners.MethodSorters; | |
@FixMethodOrder(MethodSorters.NAME_ASCENDING) | |
public class TestFizzBuzz { | |
String expected[] = { | |
"FizzBuzz", "1", "2", "Fizz", "4", "Buzz", | |
"Fizz", "7", "8", "Fizz", "Buzz", | |
"11", "Fizz", "13", "14", "FizzBuzz", | |
"16", "17", "Fizz", "19", "Buzz", | |
"Fizz", "22", "23", "Fizz", "Buzz", | |
"26", "Fizz", "28", "29", "FizzBuzz", | |
}; | |
// list of FizzBuzz methods to test | |
List<Function<Integer, String>> methods = new ArrayList<>(); | |
@Before | |
public void setup() { | |
methods.add(FizzBuzz::doFizzBuzz1); | |
methods.add(FizzBuzz::doFizzBuzz2); | |
methods.add(FizzBuzz::doFizzBuzz3); | |
} | |
@Test | |
public void doSample() { | |
for (Function<Integer, String> m: methods) { | |
for (int i = 0; i < expected.length; i++) { | |
assertEquals(expected[i], m.apply(i)); | |
} | |
} | |
} | |
@Test | |
public void testMaxRange() { | |
for (Function<Integer, String> m: methods) { | |
assertTrue(autoTest(m, Integer.MIN_VALUE, Integer.MAX_VALUE)); | |
} | |
} | |
@Test | |
public void timeAll() { | |
for (Function<Integer, String> m: methods) { | |
long start = System.nanoTime(); | |
autoTest(m, Integer.MIN_VALUE, Integer.MAX_VALUE); | |
long end = System.nanoTime(); | |
System.err.println("Time taken to run " + m.toString() + " is " + ((end - start) / 1000000000.0) +"s"); | |
} | |
} | |
private boolean autoTest(Function<Integer, String> opr, int start, int end) { | |
return IntStream | |
.range(start, end) | |
.parallel() | |
.map(i -> { | |
String r = opr.apply(i); | |
if (i % 15 == 0 && "FizzBuzz".equals(r)) { | |
return 0; | |
} else if (i % 3 == 0 && "Fizz".equals(r)) { | |
return 0; | |
} else if (i % 5 == 0 && "Buzz".equals(r)) { | |
return 0; | |
} else if (String.valueOf(i).equals(r)) { | |
return 0; | |
} | |
return 1; | |
}) | |
.filter(i -> i != 0) | |
.findFirst() | |
.isPresent() == false; | |
} | |
} |
I personally prefer the implementation of doFizzBuzz1: the logic captures the sense of "if the number is dividable by x, append y to the result". It makes future modification of adding similar logic easier without understanding the existing code. (e.g. say adding "print 'buzzbazz' if the number is divisible by 10 and 'fizzbuzzbazz' if divisible by 30" etc).
But of course, it is going to be slower as it involves creation of additional objects. But by how much? Here are some test results of running the three implementations against the whole range of integer:
Time taken to run net.clarenceho.test.TestFizzBuzz$$Lambda$1/1212899836@75a1cd57 is 175.717783485s
Time taken to run net.clarenceho.test.TestFizzBuzz$$Lambda$2/1285044316@3d012ddd is 141.0544738s
Time taken to run net.clarenceho.test.TestFizzBuzz$$Lambda$3/1607460018@6f2b958e is 140.644691232s
So it is about 25% slower. Is it worth it? Probably not in this trivial case. But worth to consider if the logic is complicated and expected to change / enhance in the future.
doFizzBuzz3 is cleaner without nested conditions and runs slightly faster possibly due to fewer branching.
JUnit code used to test the implementations. Also a good exercise to practice Java lambda and parallel streams. Tests done on AMD X4 925.