.equals()与"=="
从本质上来说,其实
.equals()与"=="并无区别。equals为Object类的方法,而“==”为java语言的基本运算符,它们对于基本数据类型的变量比较的都是它们的值,对于引用类型的对象比较的则都是指向对象的地址。
文章目录
1.Object.equals()源码
/*
* Params:obj – the reference object with which to compare.
* Returns:true if this object is the same as the obj argument; false otherwise.
* See Also:hashCode(), java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
}
可以看出,equals底层其实就是“”,所以原生的Object.equals()与“”没有任何区别,而我们所遇到的不同用法都是类重写了equals()方法。
2.String重写equals()方法
在我们的日常使用中,最常见的应该就是String类对equals的重写,源码如下
/*
*Params:anObject – The object to compare this String against
*Returns:true if the given object represents a String equivalent to this string, false otherwise
*See Also:compareTo(String), equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
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;
}
可以看出String中,首先比较的是其指向的地址是否相同,相同则返回true。
Stringnew一个对象时,会先去常量池中寻找有没有相同的字符串,如果有则只在堆中创建一个String对象,如果没有则在常量池中创建一个新的字符串常量。String.intern()方法的调用时,如果常量池中含有该字符串,则返回在该常量池中的引用;如果不存在,则在常量池中创建字符串,并返回该字符串引用。
地址不相同时,则将对象中字符串的值进行比较。
instanceof为java的保留关键字,用以判断引用类型是否相同(基本数据无法使用),返回true或false。public class InsatanceOf { public static void main(String[] args) { Father1 father = new Father1(); Son1 son = new Son1(); Son[] sons = new Son[7]; System.out.println(son instanceof Father1); //true System.out.println(father instanceof Son1); //false System.out.println(sons instanceof Father[]); //true System.out.println(sons instanceof Son[]); //true } } class Father1{} class Son1 extends Father1{}
instanceof常用于强制转换判断Father1 convert (Son1 son1){ if (son1 instanceof Father1){ return (Father1) son1; }else { return null; } }
3.重写equals的意义
在日常开发中,经常会需要重写equals方法,例如判断当对象不同时,判断对象内属性值是否相同,但一个一个判断又十分繁琐,这时在类中,重写equals方法就显得极为重要。
public class Student{
private String name;
private String age;
private String sex;
Student(String name, String age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public boolean equals(Object obj){
if (this == obj){
return true;
}
if (obj instanceof Student){
if (this.age.equals(((Student) obj).age) && this.name.equals(((Student) obj).name) && this.sex.equals(((Student) obj).sex)){
return true;
}
}
return false;
}
}
class test {
public static void main(String[] args) {
Student student1 = new Student("李四", "18", "男");
Student student2 = new Student("李四", "18", "男");
Student student3 = new Student("张三", "18", "男");
System.out.println(student1.equals(student2)); //true
System.out.println(student1.equals(student3)); //false
}
}
如上例所示,我们在Student类中重写了equals方法,在判断对象内容是否相同时,就快了很多,减少了代码冗余。
4.重写.equals()与重写hashCode
重写
equals,自然要提到hashcode,且重写equals方法,最好也重写hashcode方法。
equals和hashCode的规则
《Effective Java》中提到对equals和hashCode的规则
对于euqals应该遵守如下约定:
1、自反性:x.equals(x) 必须为true
2、对称性:如果x.equals(y),则y.euqals(x)必须为true
3、传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”
4、一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”
5、任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”
6、覆盖equals时总是要覆盖hashCode
对于hashCode应该遵守如下约定:
1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
3.如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
也就是说,hashCode()和``equals()保持一致,如果equals方法返回true,那么两个对象的hasCode()返回值必须一样。如果equals方法返回false,hashCode`可以不一样,但是这样不利于哈希表的性能,一般我们也不要这样做。
当我们重写了equals方法,并创建了两个对象,判断相等返回true的条件是属性值相等。但如果我们没有重写hashCode方法,判断equals时会默认使用Object类的hashCode方法,这样会导致属性相等的两个对象得到的hashCode却不相同,这就违背了equals的使用规则。
光说没用上代码
在java中,比较常用的hashMap、hashSet就是采用上述思想,那么为什么要重写hashCode方法呢,我们继续使用上面的student类作说明
class test2{
public static void main(String[] args) {
Student student1 = new Student("李四", "18", "男");
Student student2 = new Student("李四", "18", "男");
HashSet<Student> set = new HashSet<>();
set.add(student1);
set.add(student2);
System.out.println(set.size()); // 2
}
}
我们都知道,hashSet是不允许重复的,然而两个相同的Student对象却同时放入了set中,这是为什么呢?答案是,未重写hashCode方法导致,hashCode值是默认的Object类的hashCode方法计算得到,这也就促使程序判断该对象在set中的位置是不存在的,所以也就存入了两个相等的对象。
hashSet、hashMap的数据结构为散列表(数组+链表/红黑树的形式),在查询某个元素x时,首先,通过hashCode方法得到其hash值,再通过hash值计算出其在散列表中的数组下标位置,如果该位置上没有元素,则说明散列表中没有元素x,如果该位置上有元素(如下标1),则x会依次与该位置上的链表存放元素依次进行比较,若其中一个相等,则说明散列表中有元素x。
这里我们重写Student类hashCode方法
@Override
public int hashCode() {
System.out.println("调用了hashCode方法...");
int hashResult = 1;
hashResult = (hashResult + age.hashCode() + name.hashCode() + sex.hashCode()) * 99;
System.out.println("name:"+name +" hashCode:" + hashResult);
return hashResult;
}
控制台输出结果如下:
调用了hashCode方法...
name:李四 hashCode:86490756
调用了hashCode方法...
name:李四 hashCode:86490756
1
综上,hashMap、hashSet比较元素时,会先比较hashCode,只有当hashCode相等时,才会进一步比较元素属性或值是否相等。所以重写对象,一定要重写hashCode。


本文探讨equals()与'=='在Java中的区别,重点讲解Object.equals()源码、String类的equals重写,以及为何重写equals与hashCode的关系。通过实例揭示重写equals的意义和正确使用规则,包括一致性、自反性和hashCode的配合应用。
4153

被折叠的 条评论
为什么被折叠?



