One way to write FizzBuzz program in Java 8 using Stream API.
Write a program that prints the integers from 1 to 100. But for multiples of three print “Fizz” instead of the number, and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.
import java.util.function.IntPredicate; import java.util.function.Supplier; import java.util.stream.IntStream; import java.util.stream.Stream; public class FizzBuzz { public static void main(String[] args) { IntStream numbers = IntStream.iterate(1, el -> el + 1); IntPredicate divBy3 = number -> number % 3 == 0; IntPredicate divBy5 = number -> number % 5 == 0; IntPredicate divBy3And5 = divBy3.and(divBy5); Stream<String> fizzBuzzStream = numbers .mapToObj(String::valueOf) .map(number -> parse(number, divBy3And5, () -> "FizzBuzz")) .map(number -> parse(number, divBy3, () -> "Fizz")) .map(number -> parse(number, divBy5, () -> "Buzz")); fizzBuzzStream.limit(30).forEach(System.out::println); } private static String parse(String numberStr, IntPredicate predicate, Supplier<String> supplier) { try { int number = Integer.parseInt(numberStr); if (predicate.test(number)) { return supplier.get(); } return String.valueOf(number); } catch (NumberFormatException e) { return numberStr; } } }
The above program will run into integer overflow. You can use BigInteger to overcome that as shown below.
import java.math.BigInteger; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; public class FizzBuzzBigInteger { public static void main(String[] args) { BigInteger one = BigInteger.ONE; Stream<BigInteger> numbers = Stream.iterate(one, el -> el.add(one)); Predicate<BigInteger> divBy3 = number -> number.remainder(BigInteger.valueOf(3)) == BigInteger.ZERO; Predicate<BigInteger> divBy5 = number -> number.remainder(BigInteger.valueOf(5)) == BigInteger.ZERO; Predicate<BigInteger> divBy3And5 = divBy3.and(divBy5); Stream<String> fizzBuzzStream = numbers .map(String::valueOf) .map(number -> parse(number, divBy3And5, () -> "FizzBuzz")) .map(number -> parse(number, divBy3, () -> "Fizz")) .map(number -> parse(number, divBy5, () -> "Buzz")); fizzBuzzStream.limit(100).forEach(System.out::println); } private static String parse(String numberStr, Predicate<BigInteger> predicate, Supplier<String> supplier) { try { BigInteger number = new BigInteger(numberStr); if (predicate.test(number)) { return supplier.get(); } return numberStr; } catch (NumberFormatException e) { return numberStr; } } }
The better and more functional approach suggested(in the comments) by Dominic is shown below.
import java.math.BigInteger; import java.util.Objects; import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; public class Fizzbuzz { public static void main(String[] args) { BigInteger one = BigInteger.ONE; Predicate<BigInteger> divBy3 = number -> Objects.equals(number.remainder(BigInteger.valueOf(3)), BigInteger.ZERO); Predicate<BigInteger> divBy5 = number -> Objects.equals(number.remainder(BigInteger.valueOf(5)), BigInteger.ZERO); Predicate<BigInteger> divBy3And5 = divBy3.and(divBy5); Stream<BigInteger> numbers = Stream.iterate(one, el -> el.add(one)); Stream<String> fizzBuzzStream = numbers .map(FizzBuzzMatcher.matching(divBy3And5, () -> "FizzBuzz") .otherwise(FizzBuzzMatcher.matching(divBy3, () -> "Fizz") .otherwise(FizzBuzzMatcher.matching(divBy5, () -> "Buzz") .otherwise(BigInteger::toString)))); fizzBuzzStream.limit(100).forEach(System.out::println); } } interface FizzBuzzMatcher extends Function<BigInteger, Optional<String>> { static FizzBuzzMatcher matching(Predicate<BigInteger> matcher, Supplier<String> resultMsgSupplier) { return number -> matcher.test(number) ? Optional.of(resultMsgSupplier.get()) : Optional.empty(); } default Function<BigInteger, String> otherwise(Function<BigInteger, String> next) { return number -> apply(number).orElseGet(() -> next.apply(number)); } }
I would prefer using Optional to indicate when a predicate doesn’t match the input – something like this:
public final class InfiniteFizzBuzz {
private InfiniteFizzBuzz() {
}
public interface FizzBuzzMatcher extends Function<BigInteger, Optional> {
static FizzBuzzMatcher matching(Predicate matcher, String outputIfMatching) {
return i -> matcher.test(i) ? Optional.of(outputIfMatching) : Optional.empty();
}
default Function otherwise(Function next) {
return i -> apply(i).orElseGet(() -> next.apply(i));
}
}
private static Predicate dividesBy(int divisor) {
return i -> i.remainder(BigInteger.valueOf(divisor)).equals(BigInteger.ZERO);
}
private static final Predicate shouldFizz = dividesBy(3);
private static final Predicate shouldBuzz = dividesBy(5);
private static final Predicate shouldFizzBuzz = shouldFizz.and(shouldBuzz);
public static final Stream fizzbuzzes =
Stream.iterate(BigInteger.ONE, i -> i.add(BigInteger.ONE))
.map(FizzBuzzMatcher.matching(shouldFizzBuzz, “FizzBuzz”)
.otherwise(FizzBuzzMatcher.matching(shouldFizz, “Fizz”)
.otherwise(FizzBuzzMatcher.matching(shouldBuzz, “Buzz”)
.otherwise(BigInteger::toString))));
public static void main(String[] args) {
fizzbuzzes.limit(100).forEach(System.out::println);
}
}
Thanks Dominic for your response. I agree with you Optional approach is better than my approach.