从if-else和switch,聊聊“八股“的作用

最近在看一些八股文,有涉及到Java JVM的,也有安卓的底层原理,例如app启动流程,很多在实际业务中没有接触过,也或许是自己没有意识到,所以更多是死记硬背,所以我其实并不推崇八股文。

在实际工作中,完成各种需求其实都有很多种写法,哪怕是最简单的判断也有if-else和switch(when)可以选择,从来也没想过也可能会带来深层次的影响。

单独的if处理

最近在写的项目中,突然发现自己的一种写法,起因是因为不希望改变原有代码结构,以及将最常见的情况单独列出、单独处理,例如

kotlin 复制代码
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        mFeedbackViewModel.uiStateFlow.collect { state ->
            if (state == FeedBackState.SubmitSuccess) {

                return@collect
            }
            when (state) {
                is FeedBackState.Init -> {

                }

                is FeedBackState.SubmitLoading -> {

                }

                is FeedBackState.SubmitError -> {

                }

                else -> {}
            }

        }
    }
}

只是一种个人习惯,更多时候其实会把片段逻辑写入when(switch)中,这样也代表了代码可读性

同时,在我学习的时候就知道switch在编译成字节码时会进行优化,通过一次计算直接定位到目标分支,时间复杂度是O(1),而if-else需要逐个判断,时间复杂度是O(n)

但是今天看到一篇文章讲到一个很有意思的点,if-else在一部分情况下其实性能比switch要好很多

CPU 分支预测与指令流水线的关键作用

现代CPU的两大性能支柱

  • 分支预测:CPU 将指令执行拆分为 "取指→译码→执行→写回" 等步骤并行处理,大幅提升吞吐量;但分支指令(如 if/switch 的跳转)会打断流水线 ------CPU 需等待分支判断结果,才能确定下一条指令地址,导致流水线 "空转"。

  • 指令流水线:CPU 通过 "历史执行记录" 猜测分支走向,提前预取指令填充流水线。若预测成功,流水线无缝执行;若失败,需清空已预取的错误指令,重新加载正确指令,造成10-20 个 CPU 时钟周期的延迟(高频场景下性能损耗会被放大)。

对于像简单的if跳转,CPU是可以比较好地做分支预测的 。但是对于switch跳转,CPU则没有太多的办法。switch本质上是据索引,从地址数组里取地址再跳转

例如:

java 复制代码
// 情况 A:if-else
if (x == 100) {
  handle100();
} else if (x == 101) {
  handle101();
} else {
  handleOther();
}

// 情况 B:switch
switch(x) {
  case 100: handle100(); break;
  case 101: handle101(); break;
  default: handleOther();
}

如果是if,汇编大概是这样的逻辑:

java 复制代码
cmp x, 100
beq label_100   // 条件分支(预测器能学习到:几乎总是走)
cmp x, 101
beq label_101
jmp default

如果编译器把switch变成跳转表,汇编会变成大概这样的逻辑:

java 复制代码
idx = x - 100
if (idx < 0 || idx > (max-min)) goto default_label
target = table[idx]        // 从内存取地址
jmp target                 // 间接跳转 ------ 难预测

如果x == 100的概率是 99.9%,那么:

  • if (x==100) 这一个条件分支对预测器来说是强偏向(always true)→ 很少错,流水线几乎不清空;

  • switch 若是跳转表实现:每次都通过间接跳转到对应目标,预测器对"跳到哪个目标"不一定猜得准,导致大量间接跳转失败,流水线损耗放大,吞吐下降。

所以在"极端不均匀分布"下,通过把高频情况先写成 if,把低频情况交给 switch,能兼顾可读性和性能。

为什么处理有序数组要比非有序数组快

这是一个在stackoverflow上有一个非常有名的问题,本质上其实就是是同一个CPU分支预测原理的不同表现,只是场景不一样

假设我们写了这样的循环:

java 复制代码
for (int x : arr) {
    if (x > 100) {
        count++;
    }
}

情况 A:arr 有序(递增)

  • 前一半元素小于 100,后一半大于 100。
  • CPU 的分支预测器能很快学到模式:
    1. 在前半段几乎总是 false;
    2. 到后半段几乎总是 true;
  • 预测命中率接近 100%,流水线不需要频繁清空 → 执行很快。

情况 B:arr 无序(随机分布)

  • 每个元素大于/小于100的概率差不多,CPU无法找到规律。
  • 预测器几乎是瞎猜,预测成功率大约50%。
  • 一半时间会预测失败 → 每次失败要清空流水线,性能下降。

这就是为什么"有序数组"在某些判断场景下比"无序数组"要快。

  1. if (x == HOT) 这种情况就像有序数组中长时间连续出现同一种情况,CPU很容易预测成功。

  2. switch(x)(特别是跳转表实现)就像无序数组,每次跳转目标可能不同,CPU 很难预测,导致流水线清空更频繁。

无论是"有序数组更快"还是"提前 if 优于 switch",核心原因都是:CPU 的分支预测器喜欢"稳定可预测的模式",不喜欢"杂乱无序的跳转"。

在想

其实在最开始了解这方面的知识只是把这个事情当作一个小tips了解,看完才发现,在很多情况下性能差距可能会达到2倍。

很多的判断,例如加载状态下,其实大部分都是"加载成功"也就意味着完全可以使用if-else加上switch的方式来提升性能,而代码可读性和扩展性也可以采用其他方案权衡。

现在回过头来看,八股文其实并不是研发需要的的最终答案,作用在于提醒我们去思考为什么。如果光去背"分支少用 if-else,分支多用 switch"则毫无意义,而是真正理解了CPU的分支预测、流水线、跳转表优化,才会发现计算机的世界远远比死记硬背来的更精彩。

我不推崇八股文,因为我觉得光会背没有意义。但是我也相信八股,因为不论是代码优化、架构设计、性能调优都逃不开这些基本原理。离开了地基,再高深的设计模式、框架思维,都很容易成为空中楼阁。

参考资料:提前if判断帮助cpu分支预测

公众号:柿蒂

相关推荐
二饭1 小时前
Spring Boot 项目启动报错:MongoSocketOpenException 连接被拒绝排查日记
java·spring boot·后端
懒虫虫~2 小时前
通过内存去重替换SQL中distinct,优化SQL查询效率
java·sql·慢sql治理
鼠鼠我捏,要死了捏2 小时前
基于Redisson的分布式锁原理深度解析与性能优化实践指南
java·高并发·redisson
backordinary2 小时前
微服务学习笔记25版
java·java-ee
ZZHow10242 小时前
Maven入门_简介、安装与配置
java·笔记·maven
小蕾Java2 小时前
Java 开发工具,最新2025 IDEA使用(附详细教程)
java·ide·intellij-idea
Tans53 小时前
[小笔记] Java 集合类
java
Jerry3 小时前
Compose 5 个简短动画,让您的应用脱颖而出
android
月阳羊3 小时前
【硬件-笔试面试题-95】硬件/电子工程师,笔试面试题(知识点:RC电路中的时间常数)
java·经验分享·单片机·嵌入式硬件·面试