switch语句根据一个整数索引值进行多重分支,底层采用跳转表这种数据结构。本文整理了switch的底层原理与在C和Java中的使用规范。

参考资料

  1. Switch底层执行原理
  2. JAVA字符串类型switch的底层原理详析
  3. java基础(六) switch语句的深入解析

理解

执行switch时生成一个长度最大为case常量+1的数组,程序首先判断switch变量是否大于最大case 常量,若大于,则跳到default分支处理;否则取得数组索引号为switch变量值大小,取得数组对应值即为相应case代码块地址,程序接着跳到此地址执行,完成分支的跳转。

示例

原C代码

对于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
2
3
4
5
6
7
8
9
10
11
switch (表达式) {
case 常量表达式或枚举常量:
语句;
break;
case 常量表达式或枚举常量:
语句;
break;
......
default: 语句;
break;
}

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
2
3
4
5
6
7
8
9
10
11
12
public static void main(String [] args){
String str = "sdf";
switch (str){
case "aaa":
break;
case "ccc":
break;
case "bbb":
break;
default:
}
}

将class文件反编译得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String[] var0) {
String var1 = "sdf";
byte var3 = -1;
switch(var1.hashCode()) {
case 96321:
if (var1.equals("aaa")) {
var3 = 0;
}
break;
case 97314:
if (var1.equals("bbb")) {
var3 = 2;
}
break;
case 98307:
if (var1.equals("ccc")) {
var3 = 1;
}
}

switch(var3) {
case 1:
case 0:
case 2:
default:
}
}

可以看到,String类型的switch,转换为了字符串的哈希比较,而其哈希返回的正是int类型。hash相同的情况再通过equals方法对比字符串的值(因为两个不同的字符串 hashCode 是有可能相等的),最后引进局部变量var3再进行跳转。

留言

⬆︎TOP