天天看點

黑馬程式員_if else 為何比switch效率低

---------------------- ASP.Net+Unity開發、.Net教育訓練、期待與您交流! ----------------------

  今天聽張孝祥老師講到if else 比 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 }

這個就要挨着查表确定跳轉位置了。 

從if (a == 1) ...else if (a == 2) ...語句中我們也可以看出a 要被讀入寄存器兩次,1 和 2 分别被讀入寄存器一次。于是發現其實 a 讀兩次是有點多餘的,在你全部比較完之前隻需要一次讀入寄存器就行了,其餘都是額外開銷。但是 if 語句必須每次都把裡面的兩個數從記憶體拿出來讀到寄存器,它不知道你其實比較的是同一個 a。

1.switch用來根據一個整型值進行多路分支,并且編譯器可以對多路分支進行優化

2.switch-case隻将表達式計算一次,然後将表達式的值與每個case的值比較,進而選

  擇執行哪一個case的語句塊

3.if..else 的判斷條件範圍較廣,每條語句基本上獨立的,每次判斷時都要條件加載

  一次。

綜上所述,在多路分支時用switch比if..else if .. else結構要效率高。

---------------------- ASP.Net+Unity開發、.Net教育訓練、期待與您交流! ----------------------