Java 7+ switch(String)底层是编译期生成hashCode跳转表+运行时equals精确校验的双重机制;case必须为编译期常量,null直接抛NPE,反编译可见switch(s.hashCode())嵌套if(s.equals("xxx"))结构。

Java 中 switch 匹配 String 类型,**底层并不是直接对字符串内容做哈希匹配或逐字符比较,而是编译期优化 + 运行时哈希 + 回退校验的组合策略**。JDK 7 引入该特性后,javac 编译器会将字符串 switch 编译为等效的“先查哈希值、再比字符串内容”的两层结构。
编译期生成哈希表与跳转逻辑
当你写:
switch (str) {
case "apple": ... break;
case "banana": ... break;
case "cherry": ... break;
}javac 不会生成一堆 if (str.equals("apple")) 链,而是:
- 计算每个
case字符串的String.hashCode(),构建一个整数哈希值到序号(0,1,2…)的映射表; - 生成一个基于哈希值的
switch(int)(即对str.hashCode()做整数 switch); - 每个整数分支里,先用
==判断是否为同一对象(小概率命中),再用.equals()精确比对字符串内容,防止哈希冲突。
运行时实际执行流程
真正运行时,字节码大致等价于:
立即学习“Java免费学习笔记(深入)”;
int h = str.hashCode();
switch (h) {
case 97248: // "apple".hashCode()
if ("apple".equals(str)) { /* case body */ } else goto default;
break;
case -1406310513: // "banana".hashCode()
if ("banana".equals(str)) { /* case body */ } else goto default;
break;
...
default:
// 可能还有未覆盖的哈希值,或哈希冲突时兜底
}注意:如果多个 case 字符串哈希值相同(如 "Aa" 和 "BB" 的 hashCode 都是 2112),编译器会在对应分支内用 if-else if 逐个 equals 判断,确保语义正确。
为什么不用纯 equals 链?性能怎么保障?
纯 if (s.equals("a")) else if (s.equals("b"))... 是 O(n) 时间,而编译后的整数 switch 是 O(1) 查表(底层是 tableswitch 或 lookupswitch 字节码指令)。虽然多了哈希计算和一次 equals,但:
-
String.hashCode()是惰性计算且缓存结果(首次调用后存入hash字段),后续开销极小; - 绝大多数 case 数量不多(
- 相比反射或 Map 查找,这是零额外对象、无装箱、无 GC 压力的高效方案。
限制与注意事项
这个机制带来几个隐含约束:
-
case值必须是编译期常量字符串(即字符串字面量或static final String),因为编译器需要在编译时算出其哈希值; - 空字符串
""和null需特别注意:null会抛NullPointerException(和普通 switch 一样),不会进 default; - 不推荐在 switch 中用动态拼接的字符串(如
new String("abc")),虽语法合法,但因 hash 缓存未预热或对象不一致,可能影响分支命中率(不过语义仍正确)。


















