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