最近一部分工作就是将主模块中基础控件、业务逻辑相关代码和资源移至独立的模块(module后者叫做project)中。我们的代码中有不少switch-case里用到了R.id.xxxyyyzzz,但直接把代码移动到新的被依赖模块中时编译器却会报错:case expressions must be constant expressions!
我们都知道,switch-case语法中要求case后面要跟常量,但我们的R.id.xxxyyyzzz的确都是常量啊:
是的,它们的确都是常量,否则编译早就挂了。但资源ID一旦到了子模块中就不再是常量了:
因此,我们必须把子模块中的这些switch-case改成if-else。
不过这不是我们的重点,问题来了:挖掘机技术哪家……不,不!为什么switch-case中一定要用常量?
有同学说了,这是java语法规定!好有道理,我竟然无法反驳……
好了,我还是让代码说话吧。我们从一个简单的method来看看java是怎么处理switch-case的。
public static void switchcase(int param) {
switch (param) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
default:
}
}
public static void ifelse(int param) {
if (param == 0) {
} else if (param == 1) {
} else if (param == 2) {
}
}
相应的java字节码为:
编译器为switch-case建了一张表,而if-else却要逐个比较(iload是将局部变量压到栈顶,其他命令参照汇编语言猜着看就行J)。Tableswitch是一张key-value表,查找速度嗷嗷快——O(1),执行效率远超if-else的逐个比较。是不是脑海中浮现出了哈希表?
到这里还没完,让我们来调戏一下编译器,把switch-case里的常量改改:
publicstatic void switchcase(int param) {
switch(param) {
case0:
break;
case10:
break;
case20:
break;
case30:
break;
case40:
break;
case50:
break;
case60:
break;
default:
}
}
结果出现了一个新的指令——lookupswitch
Lookupswitch的执行与if-else类似,要逐个比较。但由于数据是排过序的,在执行过程中可以采用二分法等方式进行优化,时间复杂度也可以达到O(logn),实际效率还是很不错的。
编译器对switch-case的处理有2种方式:tableswitch和lookupswitch,那么编译器是怎么做选择的呢?答案是:对于“紧凑”(compact)的使用tableswitch,建立跳转表;对于“稀疏的”(spare)使用lookupswitch,进行比较。对于case中常量比较连续的,就认为是紧凑;常量比较分散,存在“空洞”较多的,则认为是稀疏。至于具体“紧凑”和“稀疏”的定义,就要看编译器实现者的理解了。
有人又会问:既然tableswitch效率高,干脆都用tableswitch好了,干嘛还要有个lookupswitch?好吧,我再回答这一个问题就去搬砖了。
先回忆一下我们的跳转表,一共有6个入口:
接下来再次调戏编译器,把其中2个case注释掉:
publicstatic void switchcase(int param) {
switch(param) {
case0:
break;
case1:
break;
case2:
break;
// case3:
// break;
// case4:
// break;
case5:
break;
case6:
break;
default:
}
}
再来看看结果:
依然是6个入口!只不过3、4跳转的位置和default是一样的。
因为编译器觉得这个分布还是比较“紧凑”的,只不过出现了2个“空洞”而已。为了不因小失大,它就自己补上了2个fake条目,而这2个条目的跳转位置又被设置成和default语句一致的。
如果“空洞”过大,编译器就要填上很多fake条目,从而造成空间的浪费,所以此时就转而使用lookupswitch了。
有兴趣的同学可以继续调戏编译器,看看还有什么惊喜不。
-------------------------------------原创内容,转载请说明出处-----------------------------------------
欢迎联系
微信:oscaryue001