switch-case的底层原理
switch语句根据一个整数索引值进行多重分支,底层采用跳转表这种数据结构。本文整理了switch的底层原理与在C和Java中的使用规范。
参考资料
理解
执行switch时生成一个长度最大为case常量+1的数组,程序首先判断switch变量是否大于最大case 常量,若大于,则跳到default分支处理;否则取得数组索引号为switch变量值大小,取得数组对应值即为相应case代码块地址,程序接着跳到此地址执行,完成分支的跳转。
示例
对于fall through情况(即case中没有break),采用了分离的方法,将共同的部分放入rest中。
优化
执行if-else是逐个条件进行判断,直到命中;与if-else语句相比,使用跳转表的优点是执行switch语句的时间与数量无关,且读取switch参数时只读取一次,就可跳到对应分支;缺点是维系了一个连续的数组,实际时使用空间换时间。
最大值与最小值跨度较大、且之间没有更多的条件情况下,实际申请的很多空间是没用的,这种情况下使用if-else更好。因此编译器会对case的顺序打乱,若case的值比较规律(数据差相同),就汇编成查询表;若各个case值之间非常离散,即无规律可言时,编译器会采取某种优化措施。
比如1,2,3,4,5,999这种情况也是很常见的,可以去掉最大值,去掉最小值重新测试,或者用二分法,分为2部分重新测试。
switch支持的类型
1 | switch (表达式) { |
C
因为要将case值转换为表项i,以及switch值要根据case值转换为对应数组索引,所以switch入参时只支持整形及能够转为整形的数据类型。
Java
switch 匹配的表达式可以是:
- byte、short、char、int类型及这4种类型的包装类型;
- 枚举类型;
- String 类型;
case 匹配的表达式可以是:
- 常量表达式;
- 枚举常量;
switch 底层是使用 int 型 来进行判断的,即使是枚举、String类型,最终也是转变成 int 型。由于 long 型表示范围大于 int 型,因此不支持 long 类型。
Java中支持String类型的原理
现在使用的Java的版本,基本上是都支持String类型的。
例:
1 | public static void main(String [] args){ |
将class文件反编译得到:
1 | public static void main(String[] var0) { |
可以看到,String类型的switch,转换为了字符串的哈希比较,而其哈希返回的正是int类型。hash相同的情况再通过equals方法对比字符串的值(因为两个不同的字符串 hashCode 是有可能相等的),最后引进局部变量var3再进行跳转。