Java中的equals()与==的区别与用法

简介: 【7月更文挑战第28天】
  1. 区别
    “==”操作符用于比较两个对象的地址是否相等。
    .equals() 方法用于比较两个对象的内容是否相等。
    Object 类的 .equals() 方法默认采用的是“”操作符进行比较。假如子类没有重写该方法的话,那么“”操作符和 .equals() 方法的功效就完全一样——比较两个对象的内存地址是否相等。

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

况且,字符串有字符串常量池的概念,本身就推荐使用 String s = “字符串” 这种形式来创建字符串对象,而不是通过 new 关键字的方式,因为可以把字符串缓存在字符串常量池中,方便下次使用,不用遇到 new 就在堆上开辟一块新的空间。

  1. .equals()源码
    public boolean equals(Object anObject) {
    if (this == anObject) {
     return true;
    
    }
    if (anObject instanceof String) {
     String aString = (String)anObject;
     if (coder() == aString.coder()) {
         return isLatin1() ? StringLatin1.equals(value, aString.value)
                 : StringUTF16.equals(value, aString.value);
     }
    
    }
    return false;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
首先,如果两个字符串对象的可以“==”,那就直接返回 true 了,因为这种情况下,字符串内容是必然相等的。否则就按照字符编码进行比较,分为 UTF16 和 Latin1,差别不是很大,就拿 Latin1 的来说吧。

@HotSpotIntrinsicCandidate
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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
这个 JDK 版本是 Java 17,也就是最新的 LTS(长期支持)版本。该版本中,String 类使用字节数组实现的,所以比较两个字符串的内容是否相等时,可以先比较字节数组的长度是否相等,不相等就直接返回 false;否则就遍历两个字符串的字节数组,只有有一个字节不相等,就返回 false。

这是 Java 8 中的 equals 方法源码:

public boolean equals(Object anObject) {
// 判断是否为同一对象
if (this == anObject) {
return true;
}
// 判断对象是否为 String 类型
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
// 判断字符串长度是否相等
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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
JDK 8 比 JDK 17 更容易懂一些:首先判断两个对象是否为同一个对象,如果是,则返回 true。接着,判断对象是否为 String 类型,如果不是,则返回 false。如果对象为 String 类型,则比较两个字符串的长度是否相等,如果长度不相等,则返回 false。如果长度相等,则逐个比较每个字符是否相等,如果都相等,则返回 true,否则返回 false。
“如果要进行两个字符串对象的内容比较,除了 .equals() 方法,还有其他两个可选的方案。”

1)Objects.equals()

Objects.equals() 这个静态方法的优势在于不需要在调用之前判空。

public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
1
2
3
如果直接使用 a.equals(b),则需要在调用之前对 a 进行判空,否则可能会抛出空指针 java.lang.NullPointerException。Objects.equals() 用起来就完全没有这个担心。

Objects.equals("小萝莉", new String("小" + "萝莉")) // --> true
Objects.equals(null, new String("小" + "萝莉")); // --> false
Objects.equals(null, null) // --> true

String a = null;
a.equals(new String("小" + "萝莉")); // throw exception
1
2
3
4
5
6
2)String 类的 .contentEquals()

.contentEquals() 的优势在于可以将字符串与任何的字符序列(StringBuffer、StringBuilder、String、CharSequence)进行比较。

public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
int n = cs.length();
if (n != length()) {
return false;
}
byte[] val = this.value;
if (isLatin1()) {
for (int i = 0; i < n; i++) {
if ((val[i] & 0xff) != cs.charAt(i)) {
return false;
}
}
} else {
if (!StringUTF16.contentEquals(val, cs, n)) {
return false;
}
}
return true;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
从源码上可以看得出,如果 cs 是 StringBuffer,该方法还会进行同步,非常的智能化;如果是 String 的话,其实调用的还是 equals() 方法。当然了,这也就意味着使用该方法进行比较的时候,多出来了很多步骤,性能上有些损失。

同样来看一下 JDK 8 的源码:

public boolean contentEquals(CharSequence cs) {
// argument can be any CharSequence implementation
if (cs.length() != value.length) {
return false;
}
// Argument is a StringBuffer, StringBuilder or String
if (cs instanceof AbstractStringBuilder) {
char v1[] = value;
char v2[] = ((AbstractStringBuilder)cs).getValue();
int i = 0;
int n = value.length;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
// Argument is a String
if (cs.equals(this))
return true;
// Argument is a non-String, non-AbstractStringBuilder CharSequence
char v1[] = value;
int i = 0;
int n = value.length;
while (n-- != 0) {
if (v1[i] != cs.charAt(i))
return false;
i++;
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
同样更容易理解一些:首先判断参数长度是否相等,不相等则返回 false。如果参数是 AbstractStringBuilder 的实例,则取出其 char 数组,遍历比较两个 char 数组的每个元素是否相等。如果参数是 String 的实例,则直接调用 equals 方法比较两个字符串是否相等。如果参数是其他实现了 CharSequence 接口的对象,则遍历比较两个对象的每个字符是否相等。
原文链接:https://blog.csdn.net/heromps/article/details/140674922

相关文章
|
17天前
|
存储 Java C++
java中“==”和equals,究竟比的是什么
java中“==”和equals,究竟比的是什么
27 3
java中“==”和equals,究竟比的是什么
|
3天前
|
Java 编译器 C语言
【一步一步了解Java系列】:探索Java基本类型与C语言的区别
【一步一步了解Java系列】:探索Java基本类型与C语言的区别
13 2
|
3天前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
22 1
|
8天前
|
自然语言处理 Java 数据处理
Java IO流全解析:字节流和字符流的区别与联系!
Java IO流全解析:字节流和字符流的区别与联系!
28 1
|
8天前
|
缓存 安全 Java
Java中 final、finally、finalize 有什么区别?
本文详细阐述了Java中`final`、`finally`和`finalize`的区别:`final`用于修饰类、方法和变量以表示不可变性;`finally`是用于确保在`try-catch`结构中无论是否发生异常都能执行的代码块;而`finalize`是`Object`类的方法,用于在对象被垃圾回收前执行清理工作,但在JDK 9中已被标记为弃用。
11 0
Java中 final、finally、finalize 有什么区别?
|
8天前
|
Java
Java中抽象类和接口有什么区别?
本文阐述了Java中抽象类和接口的区别,包括类型扩展、方法/属性访问控制符、方法实现、使用目的等方面的不同,并提供了抽象类和接口的使用示例,以及Java中其他类型的类(普通类、内部类等)的简介。
13 0
Java中抽象类和接口有什么区别?
|
18天前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
26 4
|
2月前
|
存储 Java
|
3月前
|
存储 Java 索引
java 中为什么重写 equals 后需要重写 hashCode
java 中为什么重写 equals 后需要重写 hashCode
62 8
|
2月前
|
存储 Java 索引