# 一.基本介绍
# 1.基本介绍
在编程中,"==" 和 "equals" 是两个经常用到的比较概念,它们的具体含义和使用场景取决于编程语言的设计和约定。
# 2."=="(相等运算符)
- "==" 是一种比较运算符,用于检查两个变量或对象是否相等。
- "==" 对于基本数据类型(如整数、浮点数等)通常比较它们的值是否相等。
- 对于引用类型(如对象),"==" 通常比较的是对象的引用,即它们是否引用了相同的内存地址。
示例(Java):
int a = 5;
int b = 5;
System.out.println(a == b); // 输出 true
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // 输出 false,因为它们是不同的对象
2
3
4
5
6
7
# 3."equals" 方法
- "equals" 是一个方法,通常被用于比较对象的内容是否相等,而不仅仅是比较引用是否相等。
- 通常需要覆盖(override)"equals" 方法来自定义对象相等的判断逻辑。
- Object 类中的 "equals" 方法默认行为是比较对象的引用,因此需要在自定义类中进行重写。
示例(Java):
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2)); // 输出 true,因为重写了 String 类的 equals 方法
2
3
"==" 通常用于比较基本数据类型的值或引用类型的引用,而 "equals" 方法通常用于比较对象的内容是否相等。在使用 "equals" 方法时,需要注意确保该方法已经被适当地重写,以符合你的比较需求。
# 二.使用案例
在 Java 中,equals
、equalsIgnoreCase
和 equalsAnyIgnoreCase
是字符串比较的三种不同方法,用于判断字符串是否相等。以下是它们的主要区别:
# 1.equals 方法
equals 方法:
equals
方法是Object
类中的方法,用于比较两个对象是否相等。- 在字符串比较中,
equals
通常被用来比较两个字符串对象的内容是否完全相同。
示例(Java):
String str1 = "hello";
String str2 = "hello";
System.out.println(str1.equals(str2)); // 输出 true
2
3
# 2.equalsIgnoreCase 方法
equalsIgnoreCase 方法:
equalsIgnoreCase
方法是String
类中的方法,用于比较两个字符串的内容是否相等,而不区分大小写。- 这意味着它会忽略字符串中的大小写差异。
示例(Java):
String str1 = "hello";
String str2 = "HELLO";
System.out.println(str1.equalsIgnoreCase(str2)); // 输出 true
2
3
# 3.equalsAnyIgnoreCase 方法
equalsAnyIgnoreCase 方法:
equalsAnyIgnoreCase
方法通常不是 Java 标准库中的方法,而是可能出现在一些自定义的库或框架中。- 它的作用类似于
equalsIgnoreCase
,但可以同时比较多个字符串是否与目标字符串相等,且忽略大小写。
示例(假设存在这样的方法):
String str = "hello";
boolean result = equalsAnyIgnoreCase(str, "HELLO", "Hi", "Hey");
System.out.println(result); // 输出 true,因为"hello"与"HELLO"相等
2
3
需要注意的是,对于 equals
和 equalsIgnoreCase
方法,建议在比较之前检查对象是否为 null
,以避免可能的 NullPointerException
。例如:
String str1 = "hello";
String str2 = // some value or possibly null
if (str1.equals(str2)) {
// do something
}
2
3
4
5
在上述代码中,如果 str2
为 null
,调用 equals
方法会导致 NullPointerException
。可以使用 Objects.equals
方法来处理这种情况:
if (Objects.equals(str1, str2)) {
// do something
}
2
3
总的来说,选择使用哪种方法取决于你的具体需求,是需要区分大小写还是忽略大小写,以及是否需要同时比较多个字符串。
# 三.面试题
# 1.比较两个对象内容是否相同
如何比较两个对象内容是否相同?(重写 equals)
例如一个 Student 类,new 两个对象出来,单纯的想比较内容是否相同如何做呢。
public class Student {
private String name;
public int age;
// get set ...
}
2
3
4
5
通过 equals() 比较两个对象是否相同,默认情况下,比较的是地址值是否相同。而比较地址值是没有意义的,所以,一般子类也会重写该方法。在诸多子类,如 String、Integer、Date 等均重写了 equals() 方法
改进思路:我们可以将比较地址值转变为比较成员变量
- 因为 name 为 String 类型,而 String 类型为引用类型,所以不能够用 == 比较,应该用 equal()
- String 中默认重写过的 equal() 方法是用来比较字符串内容是否相同
- 我们要使用的是学生类的成员变量,所以父类 Object 不能调用子类 Student 的特有功能,所以使用向下转型
//重写v1.0
public boolean equals(Object o) {
Student s = (Student) o;
if (this.name.equals(s.name) && this.age == s.age) {
return true;
} else {
return false;
}
}
2
3
4
5
6
7
8
9
//重写v2.0 (可作为最终版)
public boolean equals(Object o) {
if (this.name == o) {
return true;
}
//测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
if (!(o instanceof Student)) {
return false;
}
Student s = (Student) o;
return this.name.equals(s.name) && this.age == s.age;
}
2
3
4
5
6
7
8
9
10
11
12
// IDEA自动生成版
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
2
3
4
5
6
7
8
9
# 2.hashCode() 和 equals()
hashCode()
方法是 Object 类中的一个本地方法(用 c 语言或 c++ 实现的),会返回该对象的哈希码,也称为散列码;其本质是返回一个 int 整数。哈希码的作用是确定该对象在哈希表中的索引位置。可以通过散列码,在散列表中根据“键”快速的检索出对应的“值”。从而快速找到需要的对象,然后进行判断是不是同一个对象。
public native int hashCode();
//hashCode源码
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
equals()
方法是 Object 类中的一个方法,如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址。一般会选择重写此方法,来比较两个对象的内容是否相等,相等则返回 true。
总结:单考虑目的两者是差不多的,都是用来对比两个对象是否相等一致。
equals() 已经实现功能了,还需要 hashCode() 做什么?
重写 equals() 里面的内容一般比较全面周详,但是效率就比较低,例如:如果集合中现在已经有 2000 个元素,那么第 2001 个元素加入集合时,它就要调用 2000 次 equals 方法。
而使用 hashCode() ,其使用的哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,所以 hashCode() 这种形成 hash 码的方式比较是比较高效的。
为什么不全部使用高效率的 hashCode(),还要用 equals()?
hashCode() 方法不是一个 100% 可靠的方法,个别情况下,不同的对象生成的 hashcode 也可能会相同。
hashCode() 和 equals() 是如何一起判断保证高效又可靠的?
如果大量内容都是用 equals() 去比对,效率显然是比较低的,所以每次比对之前都去使用 hashCode() 去对比,如果返回的 hashCode 不同,代表两个对象肯定不相同,就可以直接返回结果了。如果 hashCode 相同,又为了保证其绝对可靠,所以使用 equals() 再次进行比对,同样是相同,就保证了这两个对象绝对相同。
为什么重写 equals 时必须重写 hashCode 方法?
如果重写了 equals() 而未重写 hashcode() 方法,可能就会出现两个字面数据相同的对象(例如下面 stu1 和 stu2) equals 相同(因为 equals 都是根据对象的特征进行重写的),但 hashcode 不相同的情况。
public class Student {
private String name;
public int age;
// get set ...
// 重写 equals() 不重写 hashcode()
}
--------------------------------------------
Student stu1 = new Student("BWH_Steven",22);
Student stu2 = new Student("BWH_Steven",22);
--------------------------------------------
stu1.equals(stu2); // true
stu1.hashCode(); // 和 stu2.hashCode(); 结果不一致
stu2.hashCode();
2
3
4
5
6
7
8
9
10
11
12
13
如果把对象保存到 HashTable、HashMap、HashSet 等中(不允许重复),这种情况下,去查找的时候,由于都是先使用 hashCode() 去对比,如果返回的 hashCode 不同,则会认为对象不同。可以存储,从内容上看,明显就重复了。
所以一般的地方不需要重写 hashcode() ,只有当类需要放在 HashTable、HashMap、HashSet 等 hash 结构的集合时才会去重写。
# 3.阿里巴巴 Java 开发手册
补充:阿里巴巴 Java 开发手册关于 hashCode 和 equals 的处理遵循规则:
- 只要重写 equals,就必须重写 hashCode。
- 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。
- 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals。
- String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用。