equals方法 和 hashCode方法

来源:互联网 发布:@Requestbody解析json 编辑:程序博客网 时间:2024/05/21 21:48

Object 类有equals方法和hashCode方法, 定义如下:
boolean java.lang.Object.equals(Object obj) //注意参数变量类型是Object.
public native int hashCode();

对象等同,通常指==相等。
如果对象存在“逻辑相等”,而且超类没有重写(override) equals方法以实现逻辑相等,则需要重写equals方法和hashCode方法。

Java Object 规范要求, 如果重写了equals方法,必须同时重写hashCode方法,否则可能无法查询到存储在散列集合中的对象。

两个对象equals相等,那么hashCode一定相同,反之则不成立。

如何重写equals方法和hashCode方法呢?
重写equals方法:
1. 使用==操作符检查 “参数是否为这个对象的引用”;
2. 使用instanceof检查“参数是否是正确的类型”;
3. 把参数转换成正确的类型;
4. “关键”域比较

示例: 员工类Employee 有多个域,存在这样的“逻辑相等”:
如果两个Employee 对象的“关键”域employeeId和name都相同,则这两个Employee对象相同。

public class Employee {    private int employeeId;    private String name;    private Date onboardDate;    private String title;    public Employee(int employeeId, String name) {        this.employeeId = employeeId;        this.name = name;    }    public int getEmployeeId() {        return employeeId;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Date getOnboardDate() {        return onboardDate;    }    public void setOnboardDate(Date onboardDate) {        this.onboardDate = onboardDate;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    @Override    public boolean equals(Object obj) {        if(this == obj) return true;        if(!(obj instanceof Employee)) return false;        Employee employee = (Employee)obj;        return this.employeeId == employee.employeeId &&                 (this.name == employee.name ||                    (this.name != null && this.name.equals(employee.name))                );    }    //need to override hasCode()}

重写hashCode方法:
散列码的定义影响存储在散列集合中的对象的查询效率。
一个好的散列方法通常为不相等的对象产生不相等的散列码
为每个“关键”域计算散列码,即为equals方法中涉及的域计算散列码,然后再合并。

公式: result = 31 * result + c;

c 为每个域计算的散列码:
1. boolean类型的域,则计算(f ? 1 : 0);
2. byte, char, short, int类型的域, 则计算(int)f;
3. long类型的域,则计算(int)(f^(f>>>32));
4. float类型的域,则计算Float.floatToIntBits(f);
5. double类型的域,则计算Double.doubleToLongBits(f), 然后再按照long类型值计算散列值;
6. 对象引用类型的域,则调用这个对象的散列函数
7. 数组类型的域,则调用Arrays.hashCode

示例:以上面的Employee类为例,employeeId 和 name是关键域。

    @Override    public int hashCode() {        int result = 17; //一个非0的常量值作为初始值        result = 31 * result + employeeId;        result = 31 * result + name.hashCode();        return result;    }

散列集合对象通过散列码查找对象,如果一个对象的散列码发生改变,有可能在散列集合中找不到该对象。

public class Test {    public static void main(String[] args) {        Employee e = new Employee(1, "Sunny");        List<Employee> list = new ArrayList<>();        list.add(e);        Set<Employee> set = new HashSet<>();        set.add(e);        e.setName("Tom");//change name field, then hash code will be changed        System.out.println(list.contains(e)); //print true, it is expected;        System.out.println(set.contains(e));  //print false, the object is not found due to hash code changed        set.remove(e);        System.out.println(set.size()); //print 1, not able to be removed due to hash code changed        set.add(e);         System.out.println(set.size()); //print 2, not replace the same object due to hash code changed    }}

HashSet是用HashMap来存储对象,键(key)是对象本身;

HashSet中存储的对象,对象的“关键”域(equals方法中的域)应该是不可变的 或 事实不可变的.

HashMap的键(key)如果是对象类型,此键对象的“关键”域(equals方法中的域)应该是不可变的 或 事实不可变的.

0 0
原创粉丝点击