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/

You may also like...

Leave a Reply

Your email address will not be published.