您的位置:首页 > 职场人生

面试 | 如何理解equals()方法和hashCode()

2019-06-09 12:32 218 查看
版权声明:转载注明出处 https://blog.csdn.net/nobody_1/article/details/91352016
1 equals()方法

Object类中的方法,默认检测一个对象是否等于另外一个对象,即判断两个对象是否具有相同的引用。

public class Employee {
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super.equals(obj);
}
}
public class Test {
public static void main(String[] args) {
Employee employee1 = new Employee();
Employee employee2 = new Employee();
Employee employee3 = employee1;

System.out.println(employee1.equals(employee2));   // false
System.out.println(employee1.equals(employee3));   // true
System.out.println(employee3.equals(employee2));   // false
}
}

在实际使用过程中,比较两个对象的引用往往没有任何意义,需要比较的是对象的状态,即对象的属性是否相等。所以在需要比较类之间是否相等时,都需要重写equals方法

public class Employee {
String name;
String salary;

@Override
public boolean equals(Object object) {
if (this == object) return true;  // 与自身比较返回true,同时处理null与null的比较

if (object == null) return false;

if (!(object instanceof Employee))
return false;

Employee employee = (Employee)object;
if (this.name == employee.name) return true;

return false;
}
}
public class Test {
public static void main(String[] args) {
Employee employee1 = new Employee();
employee1.name = "xiaoLi";
Employee employee2 = new Employee();
employee2.name = "xiaoLi";
Employee employee3 = new Employee();
employee3.name = "xiaoZhao";

System.out.println(employee1.equals(employee2));  // true
System.out.println(employee1.equals(employee3));  // false
System.out.println(employee3.equals(employee2));  // false
}
}
如何处理子类与父类之间equals()?

现在有一个子类Manager继承Employee类,并重写父类的equals方法:

public class Manager extends Employee {
int age;
@Override
public boolean equals(Object object) {
if (!super.equals(object)) return false;

Manager manager = (Manager) object;
return name == manager.name && age == manager.age;
}
}

但是下面的测试很有意思:

public class Test {
public static void main(String[] args) {
Employee employee1 = new Employee();
Employee employee2 = new Manager();
Manager manager = new Manager();

employee1.name = "xiaoLi";
employee2.name = "xiaoHua";
manager.name = "xiaoLi";
manager.age = 20;

System.out.println(employee1.equals(manager));   // true
System.out.println(manager.equals(employee1));    // 抛异常
}
}

manager.equals(employee1)抛出类不能转换的异常。我们知道java语言规范要求equals方法具有自反性、对称性,传递性和一致性;这里明显违反了对称性,即对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true。
那么是哪里出了问题呢?
子类Manager重写equlas(),会把object参数转换成Manager类型的对象,这里就是父类employee1转换成子类manager,明显会报异常,也就是说equals方法没有校验到object参数和this不属于同一个类的情况。即instanceof关键词并不能解决子类的问题。可以把

if (!(object instanceof Employee)) return false;

换成

if (getClass() != object.getClass()) return false;

通过类名进一步判断两个类是否相等。但具体的使用还需搭配使用场景。

所以,正确重写自定义对象的equals方法的步骤:

  • 显式参数类型应该为Object类型,以便覆盖Object类的equals方法;
  • 检测this与显式参数object是否为同一个引用; if (this == object) return true;
  • 检测object是否为null,如果是则返回fasle; if (object == null) return false;
  • 比较thsi与object是否属于同一个类;
    如果equals的语义在每个子类中有所改变,就使用getClass检测。
    if (getClass() != object.getClass()) return false;
    如果所有的子类都拥有统一的语义,就使用instanceOf检测。
    if (!(object instanceOf ClassName)) return false;
  • 将object转换为相应的类类型变量;
    ClassName objectName = (ClassName)object;
  • 对需要比较的属性进行比较,基本数据类型用==,对象类型用equals,相等返回true,否则返回false.
    注意:如果在子类中重新定义equals,则需要包含调用super.equals(other).
2 hashCode()方法

hashCode()方法也是Object类中的一个默认方法,它默认作用是返回对象的存储地址,方法返回一个整型值的散列码;如果某个对象重写了equals()方法,如上面Employee类通过name属性比较两个employee对象是否相等,由于Employee类没有重写hashCode()方法,所以即使employee1.equals(employe2)返回true,employee1和employee2的hashCode值也不一样,即在内存中可以同时存在,那么比较的意义也就不存在了。因此,重写equals()方法同时也需要重写hashCode()方法,即如果x.equals(y)返回true,那么x.hashCode()值必须与y.hashCode()值相同
看个示例:

public class HashCode {
public static void main(String[] args) {
String str = "china";
StringBuffer sBuffer = new StringBuffer(str);
System.out.println(str.hashCode() + " , " + sBuffer.hashCode());
//result: 94631255 , 366712642

String stri = new String("china");
StringBuffer sBuffer2 = new StringBuffer(stri);
System.out.println(stri.hashCode() + " , " + sBuffer2.hashCode());
//result: 94631255 , 1829164700
}
}

str对象和stri对象的hashCode值相等,这是因为String类重写了hashCode方法;而sBuffer和sBuffer2的hashCode值不相等,这是因为StringBuffer类继承Object类默认的hashCode(),返回的是对象的存储地址。
所以,如果要比较两个对象是否相等,除了重写equals()方法还需要重写hashCode()方法。注意equal()比较的属性值与计算hashCode的属性值要一致;如Employee类根据name属性判断对象是否相等,则hashCode()方法也需要根据name属性值计算hashCode值。

那么如何重写hashCode()方法呢?
hashCode方法应该返回一个整形数值,也可以是负数,并合理的组合属性的散列码,以便能够让各个不同的对象产生均匀的散列码。下面有常用的几种方法作为参考:

public int hashCode() {
return 7*Objects.hashCode(name)
+ 11*Double.hashCode(height)
+ 13*Integer.hashCode(age);
}

这个方法的好处是当name,height或者age属性为null时,hashCode值为0.否则返回参数的hashCode值。

publi
4000
c int hashCode() {
return Objects.hash(name, height, age);
}

组合各属性的散列码,计算hashCode值的方法与第一种方法类似。感兴趣的可以查看Objects类的源码。

3 ==符号

Java中,对于八大基本数据类型,= = 符号比较的是字面值;对于引用数据类型,==符号比较的是引用的地址。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: