面试 | 如何理解equals()方法和hashCode()
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中,对于八大基本数据类型,= = 符号比较的是字面值;对于引用数据类型,==符号比较的是引用的地址。
- 如何覆写java中的equals和hashcode方法
- 关于hashcode和equals方法重写的一些理解!
- equals,hashCode 方法理解
- 关于为什么要重写hashCode()方法和equals()方法及如何重写
- 如何覆盖Object类中的equals(),hashCode(),toString()方法
- 为什么要重写hashCode()方法和equals()方法以及如何进行重写
- Java如何正确重写hashCode()和equals()方法
- 为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。
- 关于为什么要重写hashCode()方法和equals()方法及如何重写
- 对象相等性——如何给自定义对象添加equals和hashCode方法
- 关于为什么要重写hashCode()方法和equals()方法及如何重写
- hashCode()、equals()以及compareTo()方法的理解
- 如何正确的重写equals() 和 hashCode()方法
- 理解Java中的hashCode和equals方法
- HashCode如何理解?关于hashcode方法和HashSet类
- equals,hashCode 方法 的重新理解
- hashCode和equals方法的理解
- equals,hashCode 方法理解
- 如何正确的重写equals() 和 hashCode()方法
- 如何重写hashCode()和equals()方法