youyichannel

志于道,据于德,依于仁,游于艺!

0%

条件执行的本质

条件语句相信大家都很熟悉了,无非就是:

if(condition) {
// code block
}

或者是 if/else if/else、三目运算符、switch 等等,但是,你是否了解条件执行的本质呢?让我们来剖析下吧。

程序最终都是一条条的指令,CPU有一个指令指示器,指向下一条要执行的指令,CPU根据指示器的指示加载指令并执行。指令大部分是具体的操作和运算,在执行这些操作时,执行完一个操作后,指令指示器会自动指向挨着的下一条指令。

但是有一些特殊的指令,称之为跳转指令,这些指令会修改指令指示器的值,让CPU跳转到一个指定的地方执行。

跳转的方式有两种:

  • 条件跳转;
  • 无条件跳转;

条件跳转会检查某个条件,满足则进行跳转,无条件跳转则是直接进行跳转。

if/else 实际上会转换为这些跳转指令。比如:

int a = 10;
if(a % 2 == 0) {
log.info("Even");
}
// other codes

转换到的跳转指令可能是:

int a = 10;
条件跳转:如果 a%2==0,跳转到第4
无条件跳转:跳转到第7
{
log.info("Even");
}
// other codes

你可能会奇怪其中的无条件跳转指令,没有它不行吗?

答案是不行,没有这条指令,不管什么条件,括号中的代码都会执行。

不过,对应的跳转指令也可能是:

int a = 10;
条件跳转:如果 a%2!=0,跳转到第6
{
log.info("Even");
}
// other codes

这个就没有无条件跳转指令,具体怎么对应和编译器实现有关。

在单一 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