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
ifand
switchcan impact performance. This article uses the JMH (Java Microbenchmark Harness) framework to compare their execution times.
JMH Setup
<code><!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
</code>We create a benchmark class with five conditional branches:
<code>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; }
}
}
</code>The benchmark results (image omitted) show that the average execution time of
switchis about 2.33 times faster than
iffor five branches.
Performance Analysis
Bytecode generated by
javacreveals why:
<code>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
...
}
}
</code>In the
switchbytecode the variable is fetched once (
getstatic #15) and the value is compared via a
tableswitch. In the
ifversion 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:
switchbecomes 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:
<code>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;
}
}
}
</code>The generated bytecode shows a
tableswitchfor the dense case set and a
lookupswitchfor the sparse set.
tableswitchworks like an array lookup (O(1)), while
lookupswitchperforms a binary search (O(log n)), making the former faster.
Benchmarking both shows that for nine branches
tableswitchis about 1.3× faster than
lookupswitch, yet both outperform
ifby a large margin.
Conclusion
For five branches,
switchis roughly 2.3× faster than
if. As the number of branches grows, the performance advantage of
switchbecomes more pronounced. The Java compiler emits
tableswitchfor compact case ranges and
lookupswitchfor sparse ones; the former is faster due to its array‑like indexing, but regardless,
switchconsistently outperforms
ifin execution speed.
Test environment: JDK 1.8 / Mac mini (2018) / IntelliJ IDEA 2020.1
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.