Just want to see by JDK8 lambda how small the program can be, my approach is using a result builder:
IntStream.rangeClosed(0 , 100).forEach(i ->{
StringBuffer bfr= new StringBuffer();
if(i % 3 == 0 )
bfr.append("Fizz");
if(i % 5 == 0 )
bfr.append("Buzz");
if(i % 3 != 0 && i % 5 != 0 )
bfr.append(i);
System.out.println(bfr.toString());
});
Can anyone try to use predicate instead ? I could not think of a way to do this.
Here are three solutions.
Java 8 with Streams:
IntStream.rangeClosed(0, 100).mapToObj(
i -> i % 3 == 0 ?
(i % 5 == 0 ? "FizzBuzz" : "Fizz") :
(i % 5 == 0 ? "Buzz" : i))
.forEach(System.out::println);
Java 8 with Eclipse Collections:
IntInterval.zeroTo(100).collect(
i -> i % 3 == 0 ?
(i % 5 == 0 ? "FizzBuzz" : "Fizz") :
(i % 5 == 0 ? "Buzz" : i))
.each(System.out::println);
Java 8 with Eclipse Collections using Predicates:
Interval.zeroTo(100).collect(
new CaseFunction<Integer, String>(Object::toString)
.addCase(i -> i % 15 == 0, e -> "FizzBuzz")
.addCase(i -> i % 3 == 0, e -> "Fizz")
.addCase(i -> i % 5 == 0, e -> "Buzz"))
.each(System.out::println);
Update:
As of the Eclipse Collections 8.0 release, the functional interfaces in Eclipse Collections now extend the equivalent functional interfaces in Java 8. This means that CaseFunction
can now be used as a java.util.function.Function
, which means it will work with Stream.map(Function)
. The following example uses CaseFunction
with a Stream<Integer>
:
IntStream.rangeClosed(0, 100).boxed().map(
new CaseFunction<Integer, String>(Object::toString)
.addCase(i -> i % 15 == 0, e -> "FizzBuzz")
.addCase(i -> i % 3 == 0, e -> "Fizz")
.addCase(i -> i % 5 == 0, e -> "Buzz"))
.forEach(System.out::println);
Update:
As of the Eclipse Collections 8.1 release, there is now support for primitive case functions. The code above can now be written as follows, removing the call to boxed
. IntCaseFunction
implements IntToObjectFunction
which extends java.util.function.IntFunction
.
IntStream.rangeClosed(0, 100).mapToObj(
new IntCaseFunction<>(Integer::toString)
.addCase(i -> i % 15 == 0, e -> "FizzBuzz")
.addCase(i -> i % 3 == 0, e -> "Fizz")
.addCase(i -> i % 5 == 0, e -> "Buzz"))
.forEach(System.out::println);
IntCaseFunction
will also work with the IntInterval
example, passed as the parameter to the collect
method.
Note: I am a committer for Eclipse Collections.
My version using format.
IntStream.rangeClosed(1, 100).forEach(
i -> System.out.format("%s%s%n",
(i % 3 == 0 ? "Fizz": ""),
(i % 5 == 0 ? "Buzz": (i % 3 == 0 ? "" : i))
)
);
Recently I was watching Kevlin Henney who was fizzbuzzing different languages on one of his talk. It got me thinking about Java 8 and none of the provided solutions were elegant for me. It is very hard to read chained ternary operators. This is what I came up with:
public class MyApproach {
private java.util.function.IntPredicate divBy(int number) {
return i -> i % number == 0;
}
public String fizzBuzz(int number) {
String result = "";
result += divBy(3).test(number) ? "Fizz" : "";
result += divBy(5).test(number) ? "Buzz" : "";
return (result).isEmpty() ? String.valueOf(number) : result;
}
public static void main(String[] args) {
MyApproach myApproach = new MyApproach();
int startInclusive = Integer.parseInt(args[0]);
int endInclusive = Integer.parseInt(args[1]);
IntStream.rangeClosed(startInclusive, endInclusive)
.mapToObj(myApproach::fizzBuzz)
.forEach(System.out::println);
}
}
This way we can throw in some test:
class FizzBuzzTest extends Specification {
def "parametrized test for fizzBuzz method" () {
given:
def myApproach = new MyApproach()
expect:
result == myApproach.fizzBuzz(num)
where:
result | num
"Fizz" | 21
"Fizz" | 123
"Buzz" | 50
"Buzz" | 250
"FizzBuzz" | 150
"13" | 13
}
}
Version with infinite streams. Origin from Haskell. Often shown in a Kevlin Henney Presentation
public static void main(String[] args) {
// creates an infinite stream with "Fizz" on every 3rd position
Stream<String> fizzes = Stream.generate(() -> new String[] {"","","Fizz"}).flatMap(arr -> Arrays.stream(arr));
// creates an infinite stream with "Buzz" on every 5th position
Iterator<String> buzzes = Stream.generate(() -> new String[] {"","","","","Buzz"}).flatMap(arr -> Arrays.stream(arr)).iterator();
// Infinite number stream as String
Iterator<String> integers = IntStream.iterate(1, x -> x + 1).mapToObj(value -> Integer.valueOf(value).toString()).iterator();
// concatenates fizzes with buzzes and return the max string from number or the concatenation
// FizzBuzz > Fizz > Buzz > 'any number as string' > ""
String[] array = fizzes.limit(100).map(fizz -> max(integers.next(), fizz+buzzes.next())).toArray(value -> new String[value]);
System.out.println(Arrays.toString(array));
}
public static String max(String val1, String val2) {
return val1.compareTo(val2) >= 0 ? val1 : val2;
}
Little more accurate:
public class FizzBuzz {
public static void main(String[] args) {
// creates an infinite stream with "Fizz" on every 3rd position
Stream<String> fizzes = Stream.generate(() -> new String[] {"","","Fizz"}).flatMap(arr -> Arrays.stream(arr));
// creates an infinite stream with "Buzz" on every 5th position
Iterator<String> buzzes = Stream.generate(() -> new String[] {"","","","","Buzz"}).flatMap(arr -> Arrays.stream(arr)).iterator();
// Concatenates fizzes with buzzes
Iterator<String> words = fizzes.map(fizz -> fizz+buzzes.next()).iterator();
// Infinite number stream as String
Stream<String> numbers = IntStream.iterate(1, x -> x + 1).mapToObj(Integer::toString);
// Choice operation word or number
BinaryOperator<String> choice1 = FizzBuzz::max;
BinaryOperator<String> choice2 = FizzBuzz::emptyAsNumberOrWord;
numbers.limit(100).map(number -> choice1.apply(number, words.next())).forEach(System.out::println);
}
public static String max(String number, String word) {
return number.compareTo(word) >= 0 ? number : word;
}
public static String emptyAsNumberOrWord(String number, String word) {
return word.isEmpty() ? number : word;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With