java switch是如何支持String類型的?

我們知道自java 1.7以後, java switch開始支持String類型。那有沒有同學思考過,java是如何支持String類型的?

我們看下面這段代碼:

public class SwitchString {
    public static void main(String[] args) {
        switch (args[0]) {
	    case "A" : break;
            case "B" : break;
            default :
	}//switch
    }
}

在JDK1.8.0_152的環境下,我們使用javac編譯SwicthString.java這個源文件。得到了SwitchString.class這個字節碼文件。而後我們利用javap反編譯SwitchString.class文件。得到下圖的結果(我直接截取的main方法的反編譯結果):

圖中,我用藍色線,圈起來的部分,出現了,65, 66字樣,而在上面又出現了,String的hashCode方法。我們知道很多高級語言的,像C,C++都是自switch誕生以來,便支持int類型的,java也不例外。String的hashCode方法返回的便是其int成員變量hash。而65則是字符'A'的ASCII碼,66是字符'B'的ASCII碼。同時65也是String類型"A"的hash值,66也是String類型的"B"的hash值。

我們是不是可以猜測,JVM就是用String的hash值這一特性,來使switch支持String類型的。

大膽假設,小心求證。我們藉助IDEA反編譯SwitchString.class字節碼文件。得到下面這份直觀的反編譯結果。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

public class SwitchString {
    public SwitchString() {
    }

    public static void main(String[] var0) {
        String var1 = var0[0];
        byte var2 = -1;
        switch(var1.hashCode()) {
        case 65:
            if (var1.equals("A")) {
                var2 = 0;
            }
            break;
        case 66:
            if (var1.equals("B")) {
                var2 = 1;
            }
        }

        switch(var2) {
        case 0:
        case 1:
        default:
        }
    }
}

通過這份直觀的結果,我們知道了jvm是先調用String的hashCode方法得到hash值,然後將case中的常量換掉。但是有個很奇怪的地方,爲什麼在case中,還要再使用String的equals方法呢?這就和String的hashCode計算hash值有關了。我附上JDK1.8.0_152中關於String的hashCode計算方法:

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

讀者如果閱讀過String的源碼,就會知道這樣,String的Hash可能會衝突,即兩個不同的String可能計算出相同的hash值。

也因此JVM纔會又用String的equals方法再次比較,並且在下面又增加了一個Switch-byte結構。

【總結】:java中switch支持String,是利用String的hash值,本質上是switch-int結構。並且利用到了equals方法來防止hash衝突的問題。最後利用switch-byte結構,精確匹配。

【思考】:string的hash衝突,在其他數據結構中,如HashMap<String, T>, HashSet<String>中是如何解決的?兩個switch結構,下面那個一定會是switch-byte結構嗎?

發佈了30 篇原創文章 · 獲贊 5 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章