版本:JDK 1.8.0_211
代码结构分析:
private final char value[]; // 字符串内部存储字符的不可变数组
public boolean equals(Object anObject) {
// 1. 引用相等检查
if (this == anObject) {
return true;
}
// 2. 类型检查
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
// 3. 长度相等检查
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 4. 逐字符比较
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true; // 所有字符匹配
}
}
return false; // 类型不匹配或长度不等
}
引用相等检查 (快速路径)
if (this == anObject) return true;
==
比较对象内存地址类型安全检查
if (anObject instanceof String) { ... }
instanceof
确保比较对象是String类型长度相等检查 (关键优化)
if (n == anotherString.value.length) { ... }
逐字符比较 (核心逻辑)
java
while (n-- != 0) {
if (v1[i] != v2[i]) return false;
i++;
}
return true;
v1/v2
缓存数组引用(避免多次访问对象成员)i
索引代替数组拷贝n
从长度递减至0(比递增少用1个变量)性能分层优化: 自引用 → 类型检查 → 长度检查 → 内容比较,层层递进过滤
内存访问优化:
java
char v1[] = value; // 缓存数组引用
char v2[] = anotherString.value;
避免多次访问成员变量,提升CPU缓存命中率
循环效率:
while (n-- != 0)
比传统 for 循环节省1个局部变量
防御性编程:
instanceof
确保类型安全后再进行类型转换
anObject != null
(因为 instanceof
在 null 时返回 false)Arrays.equals
或内在化(intrinsic)优化场景 | 时间复杂度 | 说明 |
---|---|---|
自比较 | O(1) | 引用相等直接返回 |
不同长度字符串 | O(1) | 长度检查立即返回 |
相同长度不同内容 | O(k) | k=首个不匹配字符的位置 |
完全相等 | O(n) | n=字符串长度 |
此设计完美平衡了各种场景的性能需求,体现了Java标准库对基础组件的高效实现。
Java 中 String
类的 equals
方法是字符串内容比较的核心实现,其设计在 JDK 1.8 和 JDK 17 中有显著差异。
以下从实现原理和版本对比两个维度分析,重点突出优化点和底层逻辑变化。
JDK 1.8 的 String.equals
采用 char[]
数组逐字符比较,源码如下139:
public boolean equals(Object anObject) {
if (this == anObject) return true; // 地址相同直接返回 true
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
// 长度不等直接返回 false
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 逐个比较字符
while (n-- != 0) {
if (v1[i] != v2[i]) return false;
i++;
}
return true;
}
}
return false;
}
char[]
存储字符串,通过循环逐字符比对。char[]
处理,未考虑不同编码格式(如 Latin-1 和 UTF-16)的优化可能。JDK 17 的 String.equals
引入 编码感知(Latin-1/UTF-16) 和 字节级比较优化,源码如下1:
public boolean equals(Object anObject) {
if (this == anObject) return true;
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder) // 检查编码一致性
&& StringLatin1.equals(value, aString.value); // 调用字节数组比较
}
// StringLatin1.equals 实现
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) return false; // 直接比较字节
}
return true;
}
return false;
}
coder
字段检查(COMPACT_STRINGS
启用时),确保两字符串编码格式一致(Latin-1 或 UTF-16)1。char[]
改为 byte[]
,节省内存(Latin-1 字符用 1 字节,UTF-16 用 2 字节)。StringLatin1.equals
或 StringUTF16.equals
执行比较,逻辑复用且 JVM 可针对性优化(如内联)1。下表总结核心变化:
比较维度 | JDK 1.8 | JDK 17 | 优化意义 |
---|---|---|---|
内部存储 | char[] (固定 2 字节/字符) | byte[] + 编码标记(1 或 2 字节/字符) | 内存占用降低 50%(Latin-1 场景) |
编码处理 | 无区分,统一按 char[] 比较 | 优先检查 coder 一致性 | 避免跨编码无效比较 |
比较逻辑 | 直接嵌入 while 循环逐字符比较 | 委派给 StringLatin1/StringUTF16 工具类 | 逻辑解耦,JVM 易优化 |
循环粒度 | 字符级比对(char ) | 字节级比对(byte ) | Latin-1 比较速度提升 2 倍 |
可维护性 | 代码集中,冗长 | 模块化设计,扩展性强 | 支持未来新编码格式扩展 |
byte[]
存储使纯英文文本(Latin-1)内存减半,对大型应用或微服务架构意义重大。StringLatin1
),为未来支持更多编码(如 ASCII 优化)预留接口1。char[]
和逐字符循环,忽略编码差异,适用于简单场景。建议升级 JDK 17+ 的项目直接受益于这些优化,但需注意:
重写
equals
的自定义字符串类需同步适配编码逻辑以避免行为不一致。