switch(String)语法糖解析
Java1.5之前,switch语法结构仅支持int, byte, short, char这几个基本类型(及对应的包装类型)。
1.5后提供了enum枚举结构。
Java7提供了switch(String)结构。
问题一
经常听到Java7中提供的switch(String)是Java语言的一个语法糖,实际JVM并不支持这个语法结构,但这个语法糖具体是怎么实现的,一直没有看过,今天比较有空,我来研究一下。
首先写一段switch结构的代码。
public static void main(String[] args) {
final String str = "F";
switch (str) {
case "A" :
System.out.println("A");
break;
case "B" :
System.out.println("B");
break;
default:
System.out.println("F");
}
}
查看idea反编译后的源码。
public static void main(String[] args) {
String str = "F";
String var2 = "F";
byte var3 = -1;
switch(var2.hashCode()) {
case 65:
if (var2.equals("A")) {
var3 = 0;
}
break;
case 66:
if (var2.equals("B")) {
var3 = 1;
}
}
switch(var3) {
case 0:
System.out.println("A");
break;
case 1:
System.out.println("B");
break;
default:
System.out.println("F");
}
}
可以看到,switch结构中变为了String.hashcode()方法,利用其返回的int值进行判断,所以说编译后还是使用了switch(int)结构来实现的。大家都知道String的hashcode方法是有哈希冲突的风险的,所以在每个case条件中增加了equals作为补充判断,避免哈希冲突错误。
问题二
上面说增加了equals,这样能避免哈希冲突导致问题,但哈希冲突时,比如case(“A”) ,case(“B”),加入A和B会计算出相同的hash值,那岂不是两个case的条件一样,那这样应该连编译都通不过呀。所以我找了一个哈希冲突的例子来试一下(找了半天没找到,看到一篇博客里找到了)
public static void main(String[] args) {
final 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 static void main(String[] args) {
String str = "test";
String var2 = "test";
byte var3 = -1;
switch(var2.hashCode()) {
case 2031744:
if (var2.equals("AaBB")) {
var3 = 2;
} else if (var2.equals("BBBB")) {
var3 = 1;
} else if (var2.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");
}
}
}
果然反编译后的结构发生了变化,源代码里三个相同hash值的case,编译后只有一个case,里面使用了if else的结构来做判断,这样就万无一失了,确实解决了哈希冲突的场景。
问题三
看反编译后的代码,还会有一个问题,为什么一个switch编译后会拆分为两个switch结构,为什么要新增一个switch(byte)结构呢,这样貌似不是必要的,仅仅通过上面的switch和if else结构就足以应付各种场景了。增加一个结构反而会影响性能。那么到底是什么原因导致增加一个switch(byte)结构呢。这个答案是看一篇博客找到的。
看下面这种场景
public static void main(String[] args) {
final String str = "test";
switch (str) {
case "AaAa":
System.out.println("a");
case "BBBB":
System.out.println("b");
break;
case "AaBB":
System.out.println("c");
break;
default:
System.out.println("c");
break;
}
}
public static void main(String[] args) {
String str = "test";
String var2 = "test";
byte var3 = -1;
switch(var2.hashCode()) {
case 2031744:
if (var2.equals("AaBB")) {
var3 = 2;
} else if (var2.equals("BBBB")) {
var3 = 1;
} else if (var2.equals("AaAa")) {
var3 = 0;
}
default:
switch(var3) {
case 0:
System.out.println("a");
case 1:
System.out.println("b");
break;
case 2:
System.out.println("c");
break;
default:
System.out.println("c");
}
}
}
看到这里应该就明白了,是因为switch的特殊语法结构,自上而下地处理每个条件,在不加break的时候,会继续进行下一个条件的判断。在上面那种case + if else 时就处理不了这种情况,所以第一个switch结构只是为了解决哈希冲突的问题,唯一定位一个case,下面的switch(byte)才是执行。而且这样的写法会比较清晰。
本文深入解析Java7引入的switch(String)语法糖。通过反编译代码,揭示其实现原理:使用String.hashCode()转换为int值进行判断,并在case中添加equals做精确匹配,防止哈希冲突。当出现哈希冲突时,编译器会将多个case合并为一个case,内部采用if-else结构。此外,额外的switch(byte)结构用于处理不带break的连续case执行情况,确保逻辑清晰且准确。
1193

被折叠的 条评论
为什么被折叠?



