前言-----
==,equals(), hashCode()是不是傻傻分不清, 相信我, 看完这篇博客就不会了
目录
测试类
一. 快速区分 ==和equals()的区别
二.如何把ID和Brand相同的car判定为同一对象呢?
三.什么是hashCode值
四.hashCode值的比较结果 和 equals()的比较结果在逻辑上应该一致
五.equals()和hashCode()重写时的注意事项
class Car{
String ID;//车牌号
String Brand;//品牌
String mileage //行驶的公里数
public Car(String ID,String Brand) {
this.ID = ID;
this.Brand = Brand;
}
}
package Test;
import java.util.HashMap;
public class HashMapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Car car1 = new Car("1222","AA");
Car car2 = new Car("1222","AA");
System.out.println(car1 == car2);
System.out.println(car1.equals(car2));
}
}
测试结果:
由于car1和car2在在内存中的地址不相同,所以测试结果都是false
有时,我们需要根据对象的属性值来判断对象是否相同,而不是仅仅从对象的地址来判断, 那么如何做到呢?重写equals()函数,
String 中用equals()比较字符串内容否相同,本质上也是因为重写了Object类中的equals()方法:
现在我们重写Car类的equals()方法, 只要ID和Brand相同,就视为同一个对象:
package Test;
import java.util.HashMap;
public class HashMapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Car car1 = new Car("1222","AA");
Car car2 = new Car("1222","AA");
System.out.println(car1 == car2);
System.out.println(car1.equals(car2));
}
}
class Car{
String ID;//车牌号
String Brand;//品牌
String mileage //行驶的公里数
public Car(String ID,String Brand) {
this.ID = ID;
this.Brand = Brand;
}
public boolean equals(Object obj) { //定义ID和Brand都相同的对象就是相同的
return (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
}
}
此时测试结果是false 和true, 说明equals()已经改写咯
如果把对象类比为一个学生,那么对象的地址相当于学生的身份证,是独一无二的; 而hashCode值相当于一个学生的姓名,可能会有多个对象拥有相同的hashCode值, 如同姓名相同.
package Test;
import java.util.HashMap;
public class HashMapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Car car1 = new Car("1222","AA");
Car car2 = new Car("1222","AA");
System.out.println(car1.hashCode());
System.out.println(car2.hashCode());
}
}
class Car{
String ID;//车牌号
String Brand;//品牌
String mileage //行驶的公里数
public Car(String ID,String Brand) {
this.ID = ID;
this.Brand = Brand;
}
public boolean equals(Object obj) { //定义ID和Brand都相同的对象就是相同的
return (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
}
}
测试结果:
car1的hashCode值为:2018699554
car2的hashCode值为: 1311053135
还是以上面的car1和car2为例, 我们重写了equals()的方法,car1.equals(car2)的结果true, 但是car1和car2的hashCode值却不同,用hashCode值判是否相同的话,结果会是false, 二者的结果是不相同的. 这在逻辑上是说不过去的, 所以我们应该另hashCode值的比较结果和equals()的比较结果在逻辑上一致 ,这也是一个良好的习惯.
重写hashCode方法,且必须设计一种生成机制, 在这种机制下,ID 和Brand的Car对象可以拥有相同的hashCod值, 不妨设计如下:
public int hashCode() {
return ID.hashCode() + Brand.hashCode();
}
测试demo:
import java.util.HashMap;
public class HashMapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Car car1 = new Car("1222","AA");
Car car2 = new Car("1222","AA");
System.out.println("car1的hashCode值为:" +car1.hashCode());
System.out.println("car2的hashCode值为:" +car2.hashCode());
System.out.print("用equals()的比较结果是:");
System.out.println( car1.equals(car2) );
System.out.print("用hashCode值的比较结果是:" );
System.out.println( car1.hashCode()==car2.hashCode() );
}
}
class Car{
String ID;//车牌号
String Brand;//品牌
String mileage //行驶的公里数
public Car(String ID,String Brand) {
this.ID = ID;
this.Brand = Brand;
}
public boolean equals(Object obj) { //定义ID和Brand都相同的对象就是相同的
return (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
}
public int hashCode() {
return ID.hashCode() + Brand.hashCode();
}
}
测试结果:
car1的hashCode值为:1511489
car2的hashCode值为:1511489
用equals()的比较结果是:true
用hashCode值的比较结果是:true
注:此段内容参考了该博客,感谢
第二条的内容就是上面我所解释的.
第三条的内容也好理解, 如果equals()结果是不等, 但是hashCode值有小概率是相等的.
第一条的内容: 就是说hashCode() 里面用于生成hashCode的字段最好是不容易改变的 ,以上面重写的hashCode()方法为例.
public int hashCode() {
return ID.hashCode() + Brand.hashCode();
}
这边hashCode值依靠于Car的ID和Brand这两个属性, 如果某个时候, ID或者Brand改变了,那么对于的hashCode值也会改变了,
这又有什么影响呢,不妨看段代码:
package Test;
import java.util.HashMap;
public class HashMapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
HashMap myMap = new HashMap ();
Car car1 = new Car("1222","AA");
System.out.print("此时的hashCode值为:");
System.out.println(car1.hashCode());
myMap.put(car1, "Jane");
car1.ID = "1111";
System.out.print("修改ID后,hashCode值为:");
System.out.println(car1.hashCode());
//判断是否存在key为car1的Entry
System.out.println(myMap.get(car1));
}
}
class Car{
String ID;//车牌号
String Brand;//品牌
String mileage //行驶的公里数
public Car(String ID,String Brand) {
this.ID = ID;
this.Brand = Brand;
}
public boolean equals(Object obj) { //定义ID和Brand都相同的对象就是相同的
return (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
}
public int hashCode() {
return ID.hashCode() + Brand.hashCode();
}
}
测试结果:
此时的hashCode值为:1511489
修改ID后,hashCode值为:1510496
null
熟悉HashMap的朋友应该很清楚, HashMap中的get()方法是通过比较hash值来查找的,而hash值又依赖于hashCode值,因此就不难理解为什么结果是null了. 因为HashMap中car1的hashCode值和 修改ID后的car1的hashCode值不同了
为了尽量避免这种情况, 用于生成hashCode()的字段在使用过程中应该是比较不会频繁更改的字段, 所以这也是为什么不用 mileage (行驶的公里数)来生成hashCode值的原因了, 它更容易被更改.