(一) 探讨==和equals的区别和联系?
面试考题:《关于和equals的区别》,该怎么回答完美呢?
可以这样回答:
(1)对于 == 简单来说比较的是指相等
①:对于基本数据类型,则直接比较的是对应变量存储的数值是否相等。
②:对于引用数据类型而言,比较的是其引用对象的地址是否相等。
(2)equals注意是一个方法,它对应的是判断对象是否相等,如果是基本数据类型的话无法使用,不过如果是基本数据类型对应的封装类则可以使用。
(二)为什么实体类的equals()和hashcode()要一起重写?
1、先说一下equals()方法
① 如果我们比较的对象实体类中没有对equals方法重写,那我们比较的是引用对象的地址。
② 如果实体类中对equals方法进行重写,则是按照实体类中equals的方法对两个对象进行判断是否相等,一般重写都是比较对象的内容是否相等,比如String类中的重写方法!!!
举个例子,这个例子很典型:
打印结果:
2、探讨一下HashCode的数值
Java中的对象是JVM在管理,JVM会在她认为合适的时候对对象进行移动,比如,在某些需要整理内存碎片的GC算法下发生的GC。此时,对象的地址会变动,但hashcode不会改变。就好比现实中一个人出生在A城市,成年工作后落户到了B城市,但是身份证号是不会变的。
(1)什么是hashcode?
每个对象都可以计算hashcode,首先一个对象肯定有物理地址,将对象的物理地址转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode,hashcode就是在hash表中对应的位置。
物理地址指对象存放在内存中的地址,hashcode代表对象的地址在hash表中的位置。(这里一定要注意hashcode不是对象在内存中的地址,仅仅代表了对象在hash表中的位置)
例:有一个Hash表,表中有 hashcode为1、hashcode为2…hashcode为5这样5个位置,有一个对象A,假设A的物理地址转化为整数是17,通过特定的hash函数算出来的hashcode值为2(假设用物理地址%5来计算hashcode),则对象A应该在hash表中的2的位置。如果有一个对象B,算出来的hashcode值为3,则B应该在hashcode表中3的位置。如果有一个对象C,他计算出来的hashcode也等于2,则证明C和A的物理地址是一样的(假设hash函数正确,既不同的x值算出来的h(x)不同),则对象C和对象A应该是相等的。
Java中的HashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。即在散列集合包括HashSet、HashMap以及HashTable里,对每一个存储的桶元素都有一个唯一的"块编号",即它在集合里面的存储地址;当你调用contains方法的时候,它会根据hashcode找到块的存储地址从而定位到该桶元素。
(2)判断两个对象相等可以用hashcode比较吗?
不可以,必须用equals方法,因为对于两个不同对象的hashcode值可能计算出来是相同的,不同的hashcode不同的对象一定不同。
另外一点,如果覆写了equals方法,必须覆写hashcode方法,原因是默认的hashcode是将对象的存储地址进行映射。而且逻辑上,如果两个对象的equals方法返回是相等的,那么它们的hashcode必须相等;反之不一定成立。
(3)那么重写实体类的equlas方法一定要重写hashcode方法吗?
答案是不一定,需要根据具体的情况进行判断,但是建议两个方法一起重写!
①你如果只是用来对对象进行简单的判断的话,那么你可以只重写equals方法就可以
②但是如果你使用HashSet集合进行存储数据元素的时候,那一定要对hashcode方法进行重写,否则会出现一些错误,下面举个例子:
第一种状况:只重写equals方法,不重写hashcode方法时
实体类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestUser1 {
private String name;
private Integer age;
@Override
public boolean equals(Object o) {
System.out.println("实体类重写的equals方法");
if(o == this)
return true;
if(!(o instanceof TestUser1))
return false;
if (o instanceof TestUser1) {
TestUser1 user = (TestUser1)o;
return user.name.equals(name);
//return user.name.equals(name) && user.age.equals(age);
}
return false;
}
测试方法:
package com.blog.test;
import com.blog.pojo.TestUser1;
import java.util.HashSet;
public class Test1 {
public static void main(String[] args) {
TestUser1 user1 = new TestUser1("张三");
TestUser1 user2 = new TestUser1("张三");
HashSet<TestUser1> users = new HashSet<>();
users.add(user1);
System.out.println(user1.equals(user2));
System.out.println("-----分界线---------");
System.out.println(users.contains(user2));
}
}
结果分析:
这时候如果上司交给我们的任务说是集合中不可以存在名字相同的两个人,那么这是不是出现问题了呢,这就是没有重写hashcode方法所导致的问题。
第二种状况:同时重写equals方法和hashcode方法
实体类:
package com.blog.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestUser1 {
private String name;
@Override
public boolean equals(Object o) {
System.out.println("实体类重写的equals方法");
if(o == this)
return true;
if(!(o instanceof TestUser1))
return false;
if (o instanceof TestUser1) {
TestUser1 user = (TestUser1)o;
return user.name.equals(name);
//return user.name.equals(name) && user.age.equals(age);
}
return false;
}
@Override
public int hashCode() {
System.out.println("实体类重写的hashcode方法");
result = name.hashCode();
return result;
}
}
测试方法:
public static void main(String[] args) {
TestUser1 user1 = new TestUser1("张三");
TestUser1 user2 = new TestUser1("张三");
TestUser1 user3 = new TestUser1("李四");
HashSet<TestUser1> users = new HashSet<>();
users.add(user1);
System.out.println(user1.equals(user2));
System.out.println("-----分界线---------");
System.out.println(users.contains(user2));
}
在这里我们就可以发现其实使用contains()方法的时候,他会先对hashcode值进行比较,如果两个对象的hashcode数值不对的话,会直接返回false,然后在用equals()方法进行比较。
大家有兴趣的话可以去查看一下源码,基本上也是这样,在这里由于时间关系就不一一介绍了。