Why Switch Beats If‑Else in Java: Up to 3.7× Faster (JMH Benchmark)
This article benchmarks Java's switch statement against equivalent if‑else chains using JMH, explains the underlying bytecode differences, shows how tableswitch and lookupswitch are generated, and demonstrates that switch can be up to 3.7 times faster as the number of branches grows.
switch VS if
Conditional statements are a core part of program logic, and choosing between if and switch can impact performance. This article uses the JMH (Java Microbenchmark Harness) framework to compare their execution times.
JMH Setup
<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>We create a benchmark class with five conditional branches:
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Thread)
public class SwitchOptimizeTest {
static Integer _NUM = 9;
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(SwitchOptimizeTest.class.getSimpleName())
.output("/Users/admin/Desktop/jmh-switch.log")
.build();
new Runner(opt).run();
}
@Benchmark
public void switchTest() {
int num1;
switch (_NUM) {
case 1: num1 = 1; break;
case 3: num1 = 3; break;
case 5: num1 = 5; break;
case 7: num1 = 7; break;
case 9: num1 = 9; break;
default: num1 = -1; break;
}
}
@Benchmark
public void ifTest() {
int num1;
if (_NUM == 1) { num1 = 1; }
else if (_NUM == 3) { num1 = 3; }
else if (_NUM == 5) { num1 = 5; }
else if (_NUM == 7) { num1 = 7; }
else if (_NUM == 9) { num1 = 9; }
else { num1 = -1; }
}
}The benchmark results (image omitted) show that the average execution time of switch is about 2.33 times faster than if for five branches.
Performance Analysis
Bytecode generated by javac reveals why:
public class com.example.optimize.SwitchOptimize {
static java.lang.Integer _NUM;
public static void switchTest() {
getstatic #15 // _NUM
invokevirtual #19 // intValue()
tableswitch { // 1 to 9
1: 56
2: 61
3: 66
4: 71
5: 76
6: 81
7: 87
8: 93
9: 99
default: 105
}
...
}
public static void ifTest() {
getstatic #15 // _NUM
invokevirtual #19 // intValue()
iconst_1
if_icmpne 15
...
}
}In the switch bytecode the variable is fetched once ( getstatic #15) and the value is compared via a tableswitch. In the if version the variable is fetched and compared for each branch, leading to more instructions and slower execution.
Increasing the Number of Branches
When the number of branches is increased threefold (15 cases), the performance gap widens: switch becomes about 3.7 times faster than if.
switch’s Secret: tableswitch vs lookupswitch
Depending on how compact the case values are, the compiler emits either a tableswitch (dense range) or a lookupswitch (sparse values). The following code demonstrates both:
public class SwitchOptimize {
static Integer _NUM = 1;
public static void tableSwitchTest() {
int num1;
switch (_NUM) {
case 1: num1 = 1; break;
case 2: num1 = 2; break;
case 3: num1 = 3; break;
case 4: num1 = 4; break;
case 5: num1 = 5; break;
case 6: num1 = 6; break;
case 7: num1 = 7; break;
case 8: num1 = 8; break;
case 9: num1 = 9; break;
default: num1 = -1; break;
}
}
public static void lookupSwitchTest() {
int num1;
switch (_NUM) {
case 1: num1 = 1; break;
case 11: num1 = 2; break;
case 3: num1 = 3; break;
case 4: num1 = 4; break;
case 19: num1 = 5; break;
case 6: num1 = 6; break;
case 33: num1 = 7; break;
case 8: num1 = 8; break;
case 999:num1 = 9; break;
default: num1 = -1; break;
}
}
}The generated bytecode shows a tableswitch for the dense case set and a lookupswitch for the sparse set. tableswitch works like an array lookup (O(1)), while lookupswitch performs a binary search (O(log n)), making the former faster.
Benchmarking both shows that for nine branches tableswitch is about 1.3× faster than lookupswitch, yet both outperform if by a large margin.
Conclusion
For five branches, switch is roughly 2.3× faster than if. As the number of branches grows, the performance advantage of switch becomes more pronounced. The Java compiler emits tableswitch for compact case ranges and lookupswitch for sparse ones; the former is faster due to its array‑like indexing, but regardless, switch consistently outperforms if in execution speed.
Test environment: JDK 1.8 / Mac mini (2018) / IntelliJ IDEA 2020.1
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
