switch和if-else相比

switch和if-else相比,由于使用了Binary Tree算法,绝大部分情况下switch会快一点,除非是if-else的第一个条件就为true.说实话  我也没有深入研究过这个问题的根源只是在实际开发中  没有人会去用很多很多else if的都是用 switch case 的  后者比较清晰  给人感觉就是一个脑子很清楚的人写出来的东西至于效率的本质  就让大企鹅去操心吧
编译器编译switch与编译if...else...不同。不管有多少case,都直接跳转,不需逐个比较查询。
昨天发现了一本叫做CSAPP的书,终于找到了关于switch问题的解答。

这是一段C代码: 

  1. /* $begin switch-c */  
  2. int switch_eg(int x)  
  3. {  
  4.     int result = x;  
  5.   
  6.     switch (x) {  
  7.   
  8.     case 100:  
  9.     result *= 13;  
  10.     break;  
  11.   
  12.     case 102:  
  13.     result += 10;  
  14.     /* Fall through */  
  15.   
  16.     case 103:  
  17.     result += 11;  
  18.     break;  
  19.   
  20.     case 104:  
  21.     case 106:  
  22.     result *= result;  
  23.     break;  
  24.   
  25.     default:  
  26.     result = 0;        
  27.     }  
  28.   
  29.     return result;  
  30. }  
  31. /* $end switch-c */   

用GCC汇编出来的代码如下: 

  1. .file    "switch.c"  
  2.     .version    "01.01"  
  3. gcc2_compiled.:  
  4. .text  
  5.     .align 4  
  6. .globl switch_eg  
  7.     .type     switch_eg,@function  
  8. switch_eg:  
  9.     pushl %ebp  
  10.     movl %esp,%ebp  
  11.     movl 8(%ebp),%edx  
  12.     leal -100(%edx),%eax  
  13.     cmpl ,%eax  
  14.     ja .L9  
  15.     jmp *.L10(,%eax,4)  
  16.     .p2align 4,,7  
  17. .section    .rodata  
  18.     .align 4  
  19.     .align 4  
  20. .L10:  
  21.     .long .L4  
  22.     .long .L9  
  23.     .long .L5  
  24.     .long .L6  
  25.     .long .L8  
  26.     .long .L9  
  27.     .long .L8  
  28. .text  
  29.     .p2align 4,,7  
  30. .L4:  
  31.     leal (%edx,%edx,2),%eax  
  32.     leal (%edx,%eax,4),%edx  
  33.     jmp .L3  
  34.     .p2align 4,,7  
  35. .L5:  
  36.     addl ,%edx  
  37. .L6:  
  38.     addl ,%edx  
  39.     jmp .L3  
  40.     .p2align 4,,7  
  41. .L8:  
  42.     imull %edx,%edx  
  43.     jmp .L3  
  44.     .p2align 4,,7  
  45. .L9:  
  46.     xorl %edx,%edx  
  47. .L3:  
  48.     movl %edx,%eax  
  49.     movl %ebp,%esp  
  50.     popl %ebp  
  51.     ret  
  52. .Lfe1:  
  53.     .size     switch_eg,.Lfe1-switch_eg  
  54.     .ident    "GCC: (GNU) 2.95.3 20010315 (release)"   

在上面的汇编代码中我们可以很清楚的看到switch部分被分配了一个连续的查找表,switch case中不连续的部分也被添加上了相应的条目,switch表的大小不是根据case语句的多少,而是case的最大值的最小值之间的间距。在选择相应 的分支时,会先有一个cmp子句,如果大于查找表的最大值,则跳转到default子句。而其他所有的case语句的耗时都回事O(1)。
相比于if-else结构,switch的效率绝对是要高很多的,但是switch使用查找表的方式决定了case的条件必须是一个连续的常量。而if-else则可以灵活的多。

可以看到if-else只是单纯地一个接一个比较,效率比较低 
可以看出,switch的效率一般比if-else高 
switch   效率高,     从汇编代码可以看出来   
switch   只计算一次值   然后都是test   ,   jmp,     
if...else   是每个条件都要计算一遍的.  
switch的效率与分支数无关   
当只有分支比较少的时候,if效率比switch高(因为switch有跳转表)   

分支比较多,那当然是switch

---------------------------------------------------------------------------------------------------------------------------------

switch可以进行跳转优化,java中对switch有两种处理方式,生成不同的jvm指令,一是tableswitch,一个是lookupswitch.
对于case的分支比较密集的情况,如

public class Test {

    public static void main(String[] args) {
        int i = 3;
        switch (i) {
            case 0:
                System.out.println("0");
                break;
            case 1:
                System.out.println("1");
                break;
            case 3:
                System.out.println("3");
                break;
            case 5:
                System.out.println("5");
                break;
            case 10:
                System.out.println("10");
                break;
            case 13:
                System.out.println("13");
                break;
            case 14:
                System.out.println("14");
                break;
        default:
        System.out.println("default");
                break;
        }


    }
}

使用tableswitch,得到

public static void main(java.lang.String[]);
  Code:
   0:    iconst_3
   1:    istore_1
   2:    iload_1
   3:    tableswitch{ //0 to 14
        0: 76;
        1: 87;
        2: 153;
        3: 98;
        4: 153;
        5: 109;
        6: 153;
        7: 153;
        8: 153;
        9: 153;
        10: 120;
        11: 153;
        12: 153;
        13: 131;
        14: 142;
        default: 153 }
   76:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   79:    ldc    #3; //String 0
   81:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   84:    goto    161
   87:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   90:    ldc    #5; //String 1
   92:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   95:    goto    161
   98:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   101:    ldc    #6; //String 3
   103:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   106:    goto    161
   109:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   112:    ldc    #7; //String 5
   114:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   117:    goto    161
   120:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   123:    ldc    #8; //String 10
   125:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   128:    goto    161
   131:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   134:    ldc    #9; //String 13
   136:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   139:    goto    161
   142:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   145:    ldc    #10; //String 14
   147:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   150:    goto    161
   153:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   156:    ldc    #11; //String default
   158:    invokevirtual    #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   161:    return

}

从中可以看到tableswitch使用的跳转表。它这样查找,如果case值不在//0 to 14之间,直接执行default,如果在此范围之内,则取目标值-0这一项作为目标,比如switch(i),i为3,则跳转到3-0=3,使用数组中的第三项作为目标,也就是3: 98;直接去执行98行。

如果case中的值比较稀疏,则使用lookupswitch:

public class Test2 {

    public static void main(String[] args) {
        int i = 3;
        switch (i) {
            case 3:
                System.out.println("3");
                break;
            case 20:
                System.out.println("20");
                break;
            case 50:
                System.out.println("50");
                break;
            case 100:
                System.out.println("100");
                break;
        }


    }
}

编译为

public static void main(java.lang.String[]); 
   Code: 
    0:	iconst_3 
    1:	istore_1 
    2:	iload_1 
    3:	lookupswitch{ //4 
 		3: 44; 
 		20: 55; 
 		50: 66; 
 		100: 77; 
 		default: 85 } 
    44:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    47:	ldc	#3; //String 3 
    49:	invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    52:	goto	85 
    55:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    58:	ldc	#5; //String 20 
    60:	invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    63:	goto	85 
    66:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    69:	ldc	#6; //String 50 
    71:	invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    74:	goto	85 
    77:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    80:	ldc	#7; //String 100 
    82:	invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    85:	return 

可以看到其中的
 3: lookupswitch{ //4
3: 44;
20: 55;
50: 66;
100: 77;
default: 85 }
这个就要挨着查表确定跳转位置了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值