条件语句相信大家都很熟悉了,无非就是:
if(condition) { |
或者是 if/else if/else
、三目运算符、switch
等等,但是,你是否了解条件执行的本质呢?让我们来剖析下吧。
程序最终都是一条条的指令,CPU有一个指令指示器,指向下一条要执行的指令,CPU根据指示器的指示加载指令并执行。指令大部分是具体的操作和运算,在执行这些操作时,执行完一个操作后,指令指示器会自动指向挨着的下一条指令。
但是有一些特殊的指令,称之为跳转指令,这些指令会修改指令指示器的值,让CPU跳转到一个指定的地方执行。
跳转的方式有两种:
- 条件跳转;
- 无条件跳转;
条件跳转会检查某个条件,满足则进行跳转,无条件跳转则是直接进行跳转。
if/else 实际上会转换为这些跳转指令。比如:
int a = 10; |
转换到的跳转指令可能是:
int a = 10; |
你可能会奇怪其中的无条件跳转指令,没有它不行吗?
答案是不行,没有这条指令,不管什么条件,括号中的代码都会执行。
不过,对应的跳转指令也可能是:
int a = 10; |
这个就没有无条件跳转指令,具体怎么对应和编译器实现有关。
在单一 if
的情况下可能不用无条件跳转指令,但稍微复杂一些的情况都需要。if
,
if/else
, if/else if/else
,
三元运算符都会转换为条件跳转和无条件跳转。但 switch
不太一样。
Switch 的转换和具体系统实现有关,如果分支比较少,可能会转换为跳转指令。但是如果分支比较多,使用条件跳转会进行很多次的比较运算,效率比较低,这个时候可能会使用一种更为高效的方式:跳转表。跳转表是一个映射表,存储了可能的值以及要跳转到的地址,形如:
值 | 地址 |
---|---|
值1 | 代码块1的地址 |
值2 | 代码块2的地址 |
... | ... |
值n | 代码块n的地址 |
为什么跳转表会更高效呢?
因为,跳转表中的值是整数,并且按照大小顺序排序。由于具有单调性,可以使用二分来查找。此外,如果值是连续的,跳转表还会进行特殊的优化,也就是优化成一个数组,查询的时间复杂度就是O(1)
了,因为已经事先知道对应的索引值了。即使值不是连续的,但数字比较密集,差的不多,编译器也可能会优化为一个数组型的跳转表,没有的值指向default
分支。
程序源代码中的case
值排列不要求是排序的,编译器会自动排序。
switch值的类型可以是byte, short, int, char, 枚举和String,char 的本质也是整数,枚举也有对应的整数值对应,String值会使用hashcode来表示,也是整数。
为什么不可以使用
long
呢?
因为跳转表值的存储空间一般为32位,容纳不下long
。