switch is a selection statement in Java. Unlike if/else, switch only works with constant expressions — byte, short, int, char, enum constants, and String constants (from JDK 1.7 onward).
The conventional wisdom is that switch exists for two reasons: cleaner code structure and better performance. To understand how switch actually achieves this, let’s examine its underlying implementation from the bytecode perspective.
2. Typical Use Cases
2.1 Switching on int
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
publicstaticvoidswitchInt(int flag){ switch (flag) { case1: System.out.println("This is 1"); break; case8: System.out.println("This is 8"); break; case3: System.out.println("This is 3"); break; default: System.out.println("Unknown value"); break; } }
Same pattern as byte — char values are promoted to int before comparison. The short type follows the same promotion path, so I won’t repeat the bytecode here.
2.4 Switching on enums
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
publicstaticvoidswitchEnum(PayStatusEnum flag){ switch (flag) { case INIT: System.out.println("INIT"); break; case PAYING: System.out.println("PAYING"); break; case PAID: System.out.println("PAID"); break; default: System.out.println("Invalid state"); break; } }
For enums, the compiler calls ordinal() on the enum constant, adds 1 to the result, and then switches on that int value. The compiler also generates a synthetic $SwitchMap array to map ordinals to switch indices.
2.5 Switching on strings
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
publicstaticvoidswitchString(String flag){ switch (flag) { case"ONE": System.out.println("This is ONE"); break; case"TWO": System.out.println("This is TWO"); break; case"THREE": System.out.println("This is THREE"); break; default: System.out.println("Unknown value"); break; } }
String switching uses a two-phase approach. First, it computes the hashCode of the string and uses lookupswitch to find a candidate match. When hash collisions occur (multiple strings mapping to the same hash), it falls back to String.equals for disambiguation. After identifying the matching case, it performs a second tableswitch on an assigned index to jump to the correct branch.
3. Key Characteristics
3.1 Everything boils down to int comparison
Across all the cases above, regardless of the source type, switch ultimately converts the value to int and performs integer comparison. This is the fundamental mechanism.
3.2 Why long is not supported
Since switch operates on int values internally, it doesn’t support long. The language designers didn’t extend the range to long for good reason.
The practical answer is that most selection logic involves small, finite sets of values — int covers those cases easily. A larger value range would also complicate the jump table implementation, increasing both memory footprint and lookup cost. The decision to cap at int represents a pragmatic trade-off between expressiveness and implementation complexity.
3.3 The compiler sorts the case values
One interesting pattern in the bytecode: when the case values are 1, 8, 3, the jump table lists them in sorted order — 1, 3, 8. With more case values, this ordering becomes even more apparent. The compiler sorts the branch targets to enable binary search (O(log₂n)), which significantly reduces lookup time when there are many branches.
3.4 lookupswitch vs tableswitch
The bytecode reveals two different jump instructions:
When case values are 1, 3, 8 (sparse), the compiler emits lookupswitch;
When case values are string hashes (sparse), the compiler emits lookupswitch;
When case values are ‘a’, ‘b’, ‘c’ mapping to 97, 98, 99 (dense), the compiler emits tableswitch;
When case values are enum ordinal+1 mapping to 1, 2, 3 (dense), the compiler emits tableswitch;
The pattern is clear: lookupswitch is used when case values are spread apart (high dispersion), while tableswitch is used when case values are contiguous or nearly contiguous (low dispersion). The compiler decides which instruction to emit at compile time based on the density of the case values. Both strategies aim to minimize lookup time — tableswitch uses direct indexing (O(1)), while lookupswitch uses binary search (O(log₂n)).