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
Provideris 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();-
StringBufferis used for efficient string concatenation. -
newlinestores 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. -
& 1extracts whether the bit is set. -
<< 2makes sure that if it's Fizz,ais 4, otherwiseais 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. -
& 3ensures only relevant bits are taken. -
<< 2scalesbproperly.
(3) Determining providerIndex
providerIndex = (b - a) >> 2-
providerIndexdetermines 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-
segmentIndexselects the correct FizzBuzz text fromtextSegmentsince 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!