Java中的switch语句
很多人在学习Java语言的时候,应该就听说switch语句和if else差不多,其实事实上真的是这样吗?尤其是当你真的写代码的时候,是使用一个if else还是使用switch呢?让我们带着这些问题来仔细阅读以下本文的内容吧。
首先我们来看一个简单的代码,他们实现的功能类似,但分别使用的打包开关(packed switch),稀疏开关(sparse switch)以及if else。
public int packedSwitch (int a) { switch (a) { case 0: return 7; case 1: return 3; case 2: return 4; case 3: return 0; default: return 123; } } public int sparseSwitch (int a) { switch (a) { case 70: return 7; case 13: return 3; case 26: return 4; case 63: return 0; default: return 123; } } public int ifs (int a) { if (a == 70) return 7; else if (a == 13) return 3; else if (a == 26) return 4; else if (a == 63) return 0; else return 123; } }
这段代码非常简单,也许你的老师会告诉你,这些代码最终编译出来都是一样的,那么事实上真的如此吗?其实假如你看jvm的代码的话,会发现它们其实是不相同的。我们可以使用下面命令来看jvm的代码:
javap -v CLASSNAME
上面类的的jvm代码如下所示:
Compiled from "SwitchExample.java" public class SwitchExample extends java.lang.Object SourceFile: "SwitchExample.java" minor version: 0 major version: 50 Constant pool: const #1 = Method #3.#15; // java/lang/Object."<init>":()V const #2 = class #16; // SwitchExample const #3 = class #17; // java/lang/Object const #4 = Asciz <init>; const #5 = Asciz ()V; const #6 = Asciz Code; const #7 = Asciz LineNumberTable; const #8 = Asciz packedSwitch; const #9 = Asciz (I)I; const #10 = Asciz StackMapTable; const #11 = Asciz sparseSwitch; const #12 = Asciz ifs; const #13 = Asciz SourceFile; const #14 = Asciz SwitchExample.java; const #15 = NameAndType #4:#5;// "<init>":()V const #16 = Asciz SwitchExample; const #17 = Asciz java/lang/Object; { public SwitchExample(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public int packedSwitch(int); Code: Stack=1, Locals=2, Args_size=2 0: iload_1 1: tableswitch{ //0 to 3 0: 32; 1: 35; 2: 37; 3: 39; default: 41 } 32: bipush 7 34: ireturn 35: iconst_3 36: ireturn 37: iconst_4 38: ireturn 39: iconst_0 40: ireturn 41: bipush 123 43: ireturn LineNumberTable: line 5: 0 line 6: 32 line 7: 35 line 8: 37 line 9: 39 line 10: 41 StackMapTable: number_of_entries = 5 frame_type = 32 /* same */ frame_type = 2 /* same */ frame_type = 1 /* same */ frame_type = 1 /* same */ frame_type = 1 /* same */ public int sparseSwitch(int); Code: Stack=1, Locals=2, Args_size=2 0: iload_1 1: lookupswitch{ //4 13: 47; 26: 49; 63: 51; 70: 44; default: 53 } 44: bipush 7 46: ireturn 47: iconst_3 48: ireturn 49: iconst_4 50: ireturn 51: iconst_0 52: ireturn 53: bipush 123 55: ireturn LineNumberTable: line 17: 0 line 18: 44 line 19: 47 line 20: 49 line 21: 51 line 22: 53 StackMapTable: number_of_entries = 5 frame_type = 44 /* same */ frame_type = 2 /* same */ frame_type = 1 /* same */ frame_type = 1 /* same */ frame_type = 1 /* same */ public int ifs(int); Code: Stack=2, Locals=2, Args_size=2 0: iload_1 1: bipush 70 3: if_icmpne 9 6: bipush 7 8: ireturn 9: iload_1 10: bipush 13 12: if_icmpne 17 15: iconst_3 16: ireturn 17: iload_1 18: bipush 26 20: if_icmpne 25 23: iconst_4 24: ireturn 25: iload_1 26: bipush 63 28: if_icmpne 33 31: iconst_0 32: ireturn 33: bipush 123 35: ireturn LineNumberTable: line 28: 0 line 29: 9 line 30: 17 line 31: 25 line 32: 33 StackMapTable: number_of_entries = 4 frame_type = 9 /* same */ frame_type = 7 /* same */ frame_type = 7 /* same */ frame_type = 7 /* same */ }
下面我们就来仔细看一下,首先来看if else,
public int ifs (int a) { if (a == 70) return 7; else if (a == 13) return 3; else if (a == 26) return 4; else if (a == 63) return 0; else return 123; }
public int ifs(int); Code: Stack=2, Locals=2, Args_size=2 0: iload_1 1: bipush 70 3: if_icmpne 9 6: bipush 7 8: ireturn 9: iload_1 10: bipush 13 12: if_icmpne 17 15: iconst_3 16: ireturn 17: iload_1 18: bipush 26 20: if_icmpne 25 23: iconst_4 24: ireturn 25: iload_1 26: bipush 63 28: if_icmpne 33 31: iconst_0 32: ireturn 33: bipush 123 35: ireturn
和我们想象得差不多,一个一个地进行比较,要是有相等的就return。这个应该和我们想象得比价类似吧。
下面来看switch的情况,switch有两种情况,一种就是各个switch的值比较靠近,如下:
public int packedSwitch (int a) { switch (a) { case 0: return 7; case 1: return 3; case 2: return 4; case 3: return 0; default: return 123; } }
它编译的结果如下:
public int packedSwitch(int); Code: Stack=1, Locals=2, Args_size=2 0: iload_1 1: tableswitch{ //0 to 3 0: 32; 1: 35; 2: 37; 3: 39; default: 41 } 32: bipush 7 34: ireturn 35: iconst_3 36: ireturn 37: iconst_4 38: ireturn 39: iconst_0 40: ireturn 41: bipush 123 43: ireturn
这里你会发现其实有一个tableswitch的东西,那这个是什么呢?在JVM spec中有讲到。
简单来讲,就是说其实类似做了一个key value的表,在key范围内的值,则会通过index直接找到相应的值,而范围之外,就直接返回默认值了。
而假如你使用是一个稀疏switch,也就是说各个case之间的值变化很大,他会怎么来实现呢?
public int sparseSwitch (int a) { switch (a) { case 70: return 7; case 13: return 3; case 26: return 4; case 63: return 0; default: return 123; } }
他编译的结果如下:
public int sparseSwitch(int); Code: Stack=1, Locals=2, Args_size=2 0: iload_1 1: lookupswitch{ //4 13: 47; 26: 49; 63: 51; 70: 44; default: 53 } 44: bipush 7 46: ireturn 47: iconst_3 48: ireturn 49: iconst_4 50: ireturn 51: iconst_0 52: ireturn 53: bipush 123 55: ireturn
这里我们看到他不再使用tableswitch了,它使用的是一个lookupswitch,其实说白了,他就是把所有的key排序,然后查找的时候就可以在一个有序表中进行查找了,这个效率其实相对tableSwtich就有一些差别了。当然和if else比则又不相同了。
好了,看了本文之后是不是才恍然大悟,原来我们平常看到的简单代码背后竟然有这么多不同的逻辑。
参考文章:https://www.dreamincode.net/forums/blog/1748/entry-4558-switch-statements-in-java/
Recent Comments