Skip to content

Commit cef8058

Browse files
committed
Java如何判断两个字符串是否相等?
1 parent e7d1302 commit cef8058

File tree

2 files changed

+374
-6
lines changed

2 files changed

+374
-6
lines changed

docs/string/equals.md

+81-6
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ description: Java程序员进阶之路,小白的零基础Java教程,从入
99
head:
1010
- - meta
1111
- name: keywords
12-
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,equals
12+
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,equals,java equals,java string 比较,java字符串比较
1313
---
1414

15+
# 4.8 Java如何判断两个字符串是否相等?
1516

1617
“二哥,如何比较两个字符串相等啊?”三妹问。
1718

@@ -53,11 +54,11 @@ public boolean equals(Object obj) {
5354

5455
但实际情况中,有不少类重写了 `.equals()` 方法,因为比较内存地址的要求比较严格,不太符合现实中所有的场景需求。拿 String 类来说,我们在比较字符串的时候,的确只想判断它们俩的内容是相等的就可以了,并不想比较它们俩是不是同一个对象。
5556

56-
况且,字符串有字符串常量池的概念,本身就推荐使用 `String s = "字符串"` 这种形式来创建字符串对象,而不是通过 new 关键字的方式,因为可以把字符串缓存在字符串常量池中,方便下次使用。
57+
况且,字符串有[字符串常量池](https://tobebetterjavaer.com/string/constant-pool.html)的概念,本身就推荐使用 `String s = "字符串"` 这种形式来创建字符串对象,而不是通过 new 关键字的方式,因为可以把字符串缓存在字符串常量池中,方便下次使用,不用遇到 new 就在堆上开辟一块新的空间
5758

5859
“哦,我明白了。”三妹说。
5960

60-
“那就来看一下 `.equals()` 方法的源码吧。”我说。
61+
“那就来看一下 String 类的 `.equals()` 方法的源码吧。”我说。
6162

6263
```java
6364
public boolean equals(Object anObject) {
@@ -92,7 +93,39 @@ public static boolean equals(byte[] value, byte[] other) {
9293
}
9394
```
9495

95-
我的 JDK 版本是 Java 17,也就是最新的 LTS(长期支持)版本。该版本中,String 类使用字节数组实现的,所以比较两个字符串的内容是否相等时,可以先比较字节数组的长度是否相等,不相等就直接返回 false;否则就遍历两个字符串的字节数组,只有有一个字节不相等,就返回 false。
96+
这个 JDK 版本是 Java 17,也就是最新的 LTS(长期支持)版本。该版本中,String 类使用字节数组实现的,所以比较两个字符串的内容是否相等时,可以先比较字节数组的长度是否相等,不相等就直接返回 false;否则就遍历两个字符串的字节数组,只有有一个字节不相等,就返回 false。
97+
98+
这是 Java 8 中的 equals 方法源码:
99+
100+
```java
101+
public boolean equals(Object anObject) {
102+
// 判断是否为同一对象
103+
if (this == anObject) {
104+
return true;
105+
}
106+
// 判断对象是否为 String 类型
107+
if (anObject instanceof String) {
108+
String anotherString = (String)anObject;
109+
int n = value.length;
110+
// 判断字符串长度是否相等
111+
if (n == anotherString.value.length) {
112+
char v1[] = value;
113+
char v2[] = anotherString.value;
114+
int i = 0;
115+
// 判断每个字符是否相等
116+
while (n-- != 0) {
117+
if (v1[i] != v2[i])
118+
return false;
119+
i++;
120+
}
121+
return true;
122+
}
123+
}
124+
return false;
125+
}
126+
```
127+
128+
JDK 8 比 JDK 17 更容易懂一些:首先判断两个对象是否为同一个对象,如果是,则返回 true。接着,判断对象是否为 String 类型,如果不是,则返回 false。如果对象为 String 类型,则比较两个字符串的长度是否相等,如果长度不相等,则返回 false。如果长度相等,则逐个比较每个字符是否相等,如果都相等,则返回 true,否则返回 false。
96129

97130
“嗯,二哥,这段源码不难理解。”三妹自信地说。
98131

@@ -106,8 +139,7 @@ new String("小萝莉").equals("小萝莉")
106139

107140
“输出什么呢?”我问。
108141

109-
`.equals()` 比较的是两个字符串对象的内容是否相等,所以结果为 true。”三妹答。
110-
142+
`.equals()` 比较的是两个字符串对象的内容是否相等,所以结果为 true。”三妹不假思索地答到。
111143

112144

113145
第二题:
@@ -142,6 +174,8 @@ new String("小萝莉") == new String("小萝莉")
142174

143175
“由于‘小’和‘萝莉’都在字符串常量池,所以编译器在遇到‘+’操作符的时候将其自动优化为“小萝莉”,所以返回 true。”
144176

177+
PS:至于为什么,查看这篇[String、StringBuilder、StringBuffer](https://tobebetterjavaer.com/string/builder-buffer.html)
178+
145179
第六题:
146180

147181
```java
@@ -150,6 +184,8 @@ new String("小萝莉").intern() == "小萝莉"
150184

151185
`new String("小萝莉")` 在执行的时候,会先在字符串常量池中创建对象,然后再在堆中创建对象;执行 `intern()` 方法的时候发现字符串常量池中已经有了‘小萝莉’这个对象,所以就直接返回字符串常量池中的对象引用了,那再与字符串常量池中的‘小萝莉’比较,当然会返回 true 了。”三妹说。
152186

187+
PS:[intern](https://tobebetterjavaer.com/string/intern.html) 方法我们之前已经深究过了。
188+
153189
哇,不得不说,三妹前几节的字符串相关内容都完全学会了呀!
154190

155191
“三妹,哥再给你补充一点。”我说。
@@ -220,6 +256,45 @@ public boolean contentEquals(CharSequence cs) {
220256

221257
从源码上可以看得出,如果 cs 是 StringBuffer,该方法还会进行同步,非常的智能化;如果是 String 的话,其实调用的还是 `equals()` 方法。当然了,这也就意味着使用该方法进行比较的时候,多出来了很多步骤,性能上有些损失。
222258

259+
同样来看一下 JDK 8 的源码:
260+
261+
```java
262+
public boolean contentEquals(CharSequence cs) {
263+
// argument can be any CharSequence implementation
264+
if (cs.length() != value.length) {
265+
return false;
266+
}
267+
// Argument is a StringBuffer, StringBuilder or String
268+
if (cs instanceof AbstractStringBuilder) {
269+
char v1[] = value;
270+
char v2[] = ((AbstractStringBuilder)cs).getValue();
271+
int i = 0;
272+
int n = value.length;
273+
while (n-- != 0) {
274+
if (v1[i] != v2[i])
275+
return false;
276+
i++;
277+
}
278+
return true;
279+
}
280+
// Argument is a String
281+
if (cs.equals(this))
282+
return true;
283+
// Argument is a non-String, non-AbstractStringBuilder CharSequence
284+
char v1[] = value;
285+
int i = 0;
286+
int n = value.length;
287+
while (n-- != 0) {
288+
if (v1[i] != cs.charAt(i))
289+
return false;
290+
i++;
291+
}
292+
return true;
293+
}
294+
```
295+
296+
同样更容易理解一些:首先判断参数长度是否相等,不相等则返回 false。如果参数是 AbstractStringBuilder 的实例,则取出其 char 数组,遍历比较两个 char 数组的每个元素是否相等。如果参数是 String 的实例,则直接调用 equals 方法比较两个字符串是否相等。如果参数是其他实现了 CharSequence 接口的对象,则遍历比较两个对象的每个字符是否相等。
297+
223298
“是的,总体上感觉还是 `Objects.equals()` 比较舒服。”三妹的眼睛是雪亮的,发现了这个方法的优点。
224299

225300
---

0 commit comments

Comments
 (0)