Let dissect a Java program that implements the FizzBuzz problem with a branchless
style, using a mix of bitwise operations, lambdas, and an optimized loop. Let's break it down step by step:
Overview of FizzBuzz Rules
- Print numbers from 1 to 100.
- If a number is divisible by 3, print
"Fizz"
. - If a number is divisible by 5, print
"Buzz"
. - If a number is divisible by both 3 and 5, print
"FizzBuzz"
. - Otherwise, print the number itself.
Code Breakdown
1. Defining an Interface for Lambda Expressions
interface Provider {
String value(int i);
}
- An interface
Provider
is needed since generic array are not possible, so Function or other functional interface are non allowed. - It has a single method
value(int i)
, which will be implemented using lambda expressions.
2. Creating an Array of Provider
Lambdas
Provider [] provider = {i->String.valueOf(i),i->"",i->""};
- This array holds three lambda expressions:
-
i -> String.valueOf(i)
: Returns the number as a string. -
i -> ""
: Returns an empty string. -
i -> ""
: Another empty string (used later for Fizz/Buzz handling).
-
3. Defining Text Segments
String [] textSegment = {"", "Fizz","FizzBuzz","Buzz"};
- This array maps to different text outputs:
-
textSegment[0] = ""
(for numbers that aren't Fizz or Buzz) -
textSegment[1] = "Fizz"
(for numbers divisible by 3) -
textSegment[2] = "FizzBuzz"
(for numbers divisible by both 3 and 5) -
textSegment[3] = "Buzz"
(for numbers divisible by 5)
-
4. Using StringBuilder for Efficient String Concatenation
StringBuilder sb = new StringBuilder(3000);
String newline = System.lineSeparator();
-
StringBuffer
is used for efficient string concatenation. -
newline
stores the platform-specific line separator.
5. Main Loop with Bitwise Operations
for (int i = 1, a = 0, b = 0, providerIndex = 0, segmentIndex = 0;
i <= 100;
i++,
a = ((528 >> i % 15 - 1) & 1) << 2,
b = ((-2128340926 >> ((i % 15) << 1)) & 3) << 2,
providerIndex = (b - a) >> 2,
segmentIndex = (a + b) >> 2)
{
sb.append(textSegment[segmentIndex])
.append(provider[providerIndex].value(i))
.append(newline);
}
This loop iterates from i = 1
to 100
and performs the following calculations:
(1) Calculating a
- 5 Multiples
a = ((528 >> i % 15 - 1) & 1) << 2
-
528
(binary:100001000010000
) is a bitmask that identifies numbers divisible by 5. -
(i % 15 - 1)
is used to shift the bitmask. -
& 1
extracts whether the bit is set. -
<< 2
makes sure that if it's Fizz,a
is 4, otherwisea
is 0.
(2) Calculating b
- 3 and 15 Multiples
b = ((-2128340926 >> ((i % 15) << 1)) & 3) << 2
-
-2128340926
(binary:100000010000001000000100000010
) is a bitmask that identifies numbers divisible by 3 or 15. -
((i % 15) << 1)
is used to shift the bitmask. -
& 3
ensures only relevant bits are taken. -
<< 2
scalesb
properly.
(3) Determining providerIndex
providerIndex = (b - a) >> 2
-
providerIndex
determines whether to print the number (provider[0]
) or an empty string (provider[1]
orprovider[2]
) since provides the values: 0,1,2.
(4) Determining segmentIndex
segmentIndex = (a + b) >> 2
-
segmentIndex
selects the correct FizzBuzz text fromtextSegment
since provides the values: 0,1,2,3.
6. Appending the Result
sb.append(textSegment[segmentIndex])
.append(provider[providerIndex].value(i))
.append(newline);
- The correct Fizz/Buzz text (
textSegment[segmentIndex]
) is added. - The number (or an empty string) is determined by
provider[providerIndex]
. - A newline is appended.
7. Printing the Final Output
System.out.println(sb);
- The entire FizzBuzz output is printed at once.
Key Takeaways
- Uses bitwise operations for efficient FizzBuzz checking.
- Avoids multiple if-else conditions.
- Optimizes string concatenation with
StringBuilder
. - Lambda functions simplify number/string selection.
There are more solutions about FizzBuzz, and maybe the code can be optimized further, but this code is funny and didactic enough to implement FizzBuzz in Java!
Top comments (0)