Java如何比较两个字符串(对象)是否相等
看似简单的问题,可以引申为操作符==
和equals()
方法有什么区别?
==
操作符用于比较两个对象的地址是否相等equals()
用于比较两个对象的内容是否相等// String对象比较
String alita=new String(“小萝莉”);
String luolita=new String(“小萝莉”);
System.out.println(alita.equals(luolita)); // true
System.out.println(alita == luolita); // false
.equals()
输出的结果为 true,而“==
”操作符输出的结果为 false
前者要求内容相等就可以,后者要求必须是同一个对象。
Java 的所有类都默认地继承 Object 这个超类,该类有一个名为 .equals()
的方法。
Object的源码
public boolean equals(Object obj) {
return (this == obj);
}
Object 类的 .equals()
方法默认采用的是“==
”操作符进行比较。
假如子类没有重写该方法的话,那么“==
”操作符和 .equals()
方法的功效就完全一样——比较两个对象的内存地址是否相等。
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;
}
首先,如果两个字符串对象的可以“==”,那就直接返回 true 了,因为这种情况下,字符串内容是必然相等的。否则就按照字符编码进行比较,分为 UTF16 和 Latin1,差别不是很大,就拿 Latin1 的来说吧。
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;
}
我的 JDK 版本是 Java 11,也就是最新的 LTS(长期支持)版本。该版本中,String 类使用字节数组实现的,所以比较两个字符串的内容是否相等时,可以先比较字节数组的长度是否相等,不相等就直接返回 false;否则就遍历两个字符串的字节数组,只要有一个字节不相等,就返回 false。
第一题:
new String("小萝莉").equals("小萝莉")
.equals()
比较的是两个字符串对象的内容是否相等,所以结果为 true。
第二题:
new String("小萝莉") == "小萝莉"
==操作符左侧的是在堆中创建的对象,右侧是在字符串常量池中的对象,尽管内容相同,但内存地址不同,所以返回 false。
第三题:
new String("小萝莉") == new String("小萝莉")
new 出来的对象肯定是完全不同的内存地址,所以返回 false。
第四题:
"小萝莉" == "小萝莉"
字符串常量池中只会有一个相同内容的对象,所以返回 true
第五题:
"小萝莉" == "小" + "萝莉"
由于‘小’和‘萝莉’都在字符串常量池,所以编译器在遇到‘+’操作符的时候将其自动优化为“小萝莉”,所以返回 true。
第六题:
new String("小萝莉").intern() == "小萝莉"
new String(“小萝莉”) 在执行的时候,会先在字符串常量池中创建对象,然后再在堆中创建对象;执行 intern()
方法的时候发现字符串常量池中已经有了‘小萝莉’这个对象,所以就直接返回字符串常量池中的对象引用了,那再与字符串常量池中的‘小萝莉’比较,当然会返回 true
如果要进行两个字符串对象的内容比较,除了 .equals()
方法,还有其他两个可选的方案。
1)Objects.equals()
Objects.equals()
这个静态方法的优势在于不需要在调用之前判空。
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
如果直接使用 a.equals(b)
,则需要在调用之前对 a 进行判空,否则可能会抛出空指针 java.lang.NullPointerException
。
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
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;
}
从源码上可以看得出,如果 cs 是 StringBuffer,该方法还会进行同步,非常的智能化;如果是 String 的话,其实调用的还是 equals() 方法。
自定义对象的比较
上面比较的String是系统自带的,下面我们来看看如果是自己定义类,使用equals还可以吗?
package com.study;
import java.util.Objects;
/** * @Description TODO * @Classname Person * @Date 2021/8/22 14:45 * @Created by 折腾的小飞 */
public class Person {
private String name; // 名字
private int age; // 年龄
// 无参构造
public Person() {
}
// 有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// get方法和set方法,用来得到和设置成员变量的值
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// toString方法,用来打印
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 自定义对象的比较
Person p1 = new Person("卓卓", 22);
Person p2 = new Person("卓卓", 22);
System.out.println(p1 == p2); // faslse
System.out.println(p1.equals(p2)); //false
怎么回事呢?
怎么使用了equals比较还是false呢?
ctrl+鼠标左键点击进去发现
public boolean equals(Object obj) {
return (this == obj);
}
当是同一个对象时,返回true;不是一个对象,返回false。
我们发现它还是比较的地址,怎么办呢?
我们需要重写equals()方法,让它去比较对象的内容
重写了的equals()方法如下:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
第一个,判断,是否为同一个对象
第二个,判断传入的对象是否为空,类名是否相等
第三个,判断属性值是否相等
还有一个hashCode()
方法,用于比较对象的hash值是否相同
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
发现两个对象的内容相同,地址不同。
如果我们要比较内容的hashCode值呢?
我们也可以重写hashCode()
方法,去比较对象内容的hash值
@Override
public int hashCode() {
return Objects.hash(name, age);
}
所以,比较自定义对象时,需要重写equals()
和hashCode()
方法
还没有评论,来说两句吧...