java中string的哈希比对是编译器在switch语句中的语法糖,编译期计算hashcode并固化字节码,运行时先哈希再equals校验;equals不依赖hashcode,但哈希结构要求equals相等则hashcode必相等。

传入 String 参数时的哈希比对,不是面向对象重构本身的机制,而是 Java 编译器和运行时在特定上下文(如 switch 语句)中对字符串处理的实现细节。重构过程本身不改变底层比对逻辑,但会影响你能否安全、高效地使用这些机制。
switch 中 String 参数的哈希比对是编译期决定的
Java 7+ 支持 switch(String),但这并非 JVM 原生能力,而是编译器做的“语法糖”转换:
- 所有
case "xxx"必须是编译期常量(字面量或static final String),编译器在编译时就计算出每个 case 的hashCode(),并固化进字节码; - 运行时只调用一次
s.hashCode(),然后跳转到对应分支; - 每个分支内部仍会执行
s.equals("xxx"),防止哈希冲突(比如"Aa"和"BB"的 hash 都是 2112); - 若传入
null,直接抛NullPointerException,根本不会进入 switch 主体。
equals() 不依赖 hashCode(),但 hashCode() 影响容器性能
String.equals() 的实现完全基于字符逐位比较,不调用 hashCode():
- 先判引用相等(
this == obj); - 再判类型、长度;
- 最后循环比较
char[]中每个字符; -
hashCode()只在HashMap、HashSet等哈希结构中用于快速定位桶位置,它和equals()是协同关系:-
x.equals(y)成立 →x.hashCode() == y.hashCode()必须成立; -
x.hashCode() == y.hashCode()成立 →x.equals(y)不一定成立(哈希碰撞)。
-
重构时要注意字符串常量性与哈希稳定性
当你在重构中把字符串提取为变量或动态拼接,可能破坏 switch 或哈希容器的预期行为:
-
case s(变量)编译失败,因为s不是编译期常量; -
case "a" + getSuffix()编译失败,含运行时表达式; - 使用
new String("abc")创建的字符串,虽然内容相同,但hashCode()值一样(因为String的hashCode()只跟字符内容有关),不影响HashMap查找,但==判断会失败; - 若重写自定义类的
equals(),必须同步重写hashCode(),否则放入HashSet会出现重复或丢失。
字符串哈希值的计算方式固定且可预测
String.hashCode() 的公式是:
h = 31 * h + val[i]
其中 val[] 是内部 char 数组,初始 h = 0。这个算法保证:
- 相同内容的字符串,无论创建方式如何(字面量、
new、intern()),hashCode()一定相同; - 不同内容字符串可能哈希冲突,所以
equals()不可省略; - 31 是奇素数,兼顾分布均匀性和位运算效率(
31 * i == (i )。











