深入理解JAVA中的switch case是如何对String做支持的

386 阅读2分钟
原文链接: zhuanlan.zhihu.com
本文主要研究在Java中,switch case语法是如何对String进行支持的

先看原来的代码

public class Test {
    public static void main(String[] args) {
        String str = "test";
        switch (str) {
        case "a":
            System.out.println("a");
            break;
        case "b":
            System.out.println("b");
            break;
        case "c":
            System.out.println("c");
            break;
        default:
            System.out.println("c");
            break;
        }
    }
}

然后再看反编译之后的代码

public class Test {
    public Test() {
    }
    public static void main(String[] args) {
        String str = "test";
        byte var3 = -1;
        switch(str.hashCode()) {
        case 97:
            if(str.equals("a")) {
                var3 = 0;
            }
            break;
        case 98:
            if(str.equals("b")) {
                var3 = 1;
            }
            break;
        case 99:
            if(str.equals("c")) {
                var3 = 2;
            }
        }
        switch(var3) {
        case 0:
            System.out.println("a");
            break;
        case 1:
            System.out.println("b");
            break;
        case 2:
            System.out.println("c");
            break;
        default:
            System.out.println("c");
        }
    }
}

在上面我们可以看到,在比较的时候,先是通过hashcode来比较,如果hashcode一样,就再通过equals方法来比较。所以本质上还是没有脱离int比较的原则。

综上所述,java的switch对String的支持,实际上是通过编译器做了一次优化

那么如果两个case的String的hashcode冲突^1了会怎么样呢?比如下面代码:

public class Test {
    public static void main(String[] args) throws Exception {
        String str = "test";
        switch (str) {
        case "AaAa":
            System.out.println("a");
            break;
        case "BBBB":
            System.out.println("b");
            break;
        case "AaBB":
            System.out.println("c");
            break;
        default:
            System.out.println("c");
            break;
        }
    }
}

再看反编译之后的代码

public class Test {
    public Test() {
    }
    public static void main(String[] args) throws Exception {
        String str = "test";
        byte var3 = -1;
        switch(str.hashCode()) {
        case 2031744:
            if(str.equals("AaBB")) {
                var3 = 2;
            } else if(str.equals("BBBB")) {
                var3 = 1;
            } else if(str.equals("AaAa")) {
                var3 = 0;
            }
        default:
            switch(var3) {
            case 0:
                System.out.println("a");
                break;
            case 1:
                System.out.println("b");
                break;
            case 2:
                System.out.println("c");
                break;
            default:
                System.out.println("c");
            }
        }
    }
}

编译器会在同一个hashcode下面,再通过if else来进行判断。

那么可能还会有一个问题,为什么要分成两步的switch来做呢?其实很简单,方便给编译器定一个规则。我们设想一下,如果不是通过两步switch,那么应该是如下的代码:

public class Test {
    public Test() {
    }
    public static void main(String[] args) throws Exception {
        String str = "test";
        byte var3 = -1;
        switch(str.hashCode()) {
        case 2031744:
            if(str.equals("AaBB")) {
                System.out.println("a");
            } else if(str.equals("BBBB")) {
                System.out.println("b");
            } else if(str.equals("AaAa")) {
                System.out.println("c");
            }
        default:
            System.out.println("c");
        }
    }
}

那么如果我们的case "AaAa":是没有break条件的,那么编译器又要做额外的优化才能达到这个效果,这样子的话,对编译器的编写会十分复杂,不如就分为两步,第一步的switch先计算出要走哪个case,然后再在第二个switch去执行具体的case。

原文:深入理解JAVA中的switch case是如何对String做支持的