开发教程您现在的位置:主页 > 开发教程 >

Java中equals方法和hashcode方法的区别

发布日期:2018-01-09 09:06

Java中能够正确的自定义比较对象,西安Java培训在以下总结出怎样的自定义对象,重写equals方法和hashcode方法。
在实践使用中经常会比较两个目标是否持平,比方下面的Address类,它有两个特点:String province 和 String city。
public class Address {
    private String province;
    private String city;
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public Address() {}
    public Address(String province, String city) {this.province = province; this.city = city;}

在现实生活中我们以为若两个 Address 的 province 和 city 特点相同,则它们应该是同一个地址(省市都一样,当然就是同一个地区啦)。但下面的代码却表明:address1 和 address2 是两个“不同的地址”
public class TestAddress {
public static void main(String[] args) {
Address address1 = new Address("广东","广州");
Address address2 = new Address("广东", "广州");
System.out.println(address1 == address2);//false System.out.println(address1.equals(address2));//false
System.out.println(address1.hashCode() == address2.hashCode());//false
}
}

其原因是:在JAVA(编程言语)中比较目标 和 现实生活中 比较两个目标是两个不同的概念,前者暂且称为“物理相等”,而后者是“逻辑相等”。
adress1==adress2 是依据两个目标的内存地址是否相同进行比较的,第4、5行别离 new 了两个目标,这两个目标存储在内存中不同的地址,当然不会相等了。
因为Address类并没有重写equals办法,那么address1.equals(address2) 履行的就是Object类的equals办法,而java.lang.Object类的equlas办法是这样完成的:
 
    public boolean equals(Object obj) {
        return (this == obj);
    }
JDK中对该办法的注释如下:也就是说:Object类的equals办法 是经过 == 来比较两个目标是否持平的,也即依据 目标x引证 和 目标y 的引证是否指向内存中的同一个地址 来判别 目标x 和 目标y 是否持平。     
* The {@code equals} method for class {@code Object} implements
     * the most discriminating possible equivalence relation on objects;
     * that is, for any non-null reference values {@code x} and
     * {@code y}, this method returns {@code true} if and only
     * if {@code x} and {@code y} refer to the same object
     * ({@code x == y} has the value {@code true}).
 
而依照现实思想,已然 adress1 和 address2 都代表广东广州,那么在程序中它们两个目标比较就应该持平(逻辑上持平),也即address1.equals(address2)应该返回true才对。所以就需求掩盖 Object 类中的 equals 办法 和 hashCode办法了。
而掩盖 equals办法 和hashCode办法是需求技巧的。
①掩盖了Object类的equals办法后,需求再掩盖 Object类的hashCode办法。为什么呢?----不要违背“hashCode方法的 经过约好(general contract) ”
Object类的equals办法上的注释如下:
   / * Note that it is generally necessary to override the {@code hashCode}
     * method whenever this method is overridden, so as to maintain the
     * general contract for the {@code hashCode} method, which states
     * that equal objects must have equal hash codes.
    */
    public boolean equals(Object obj) {
        return (this == obj);
    } 
而这个“通用约好”就是:1.若两个方针依据equals(Object)办法比较持平,那么调用这两个方针中恣意一个方针的hashCode办法 都必须 发生同样的整数成果。
2.若两个方针依据equals(Object)办法比较不持平,那么调用这两个方针中恣意一个方针的hashCode办法 能够 发生相同的整数成果,可是最好 发生不同的整数成果,这样能够供给散列表的功能(当要把Address类 作为 键 put 到HashMap中时,能够削减抵触,可参阅这篇文章)
那详细怎么正确地掩盖equals()呢?《Effective JAVA》里边给出了办法,套路是一样的,其方针是确保:自反性、对称性、一致性。总归,对于上面的Address类而言,能够这样:
@Override
public boolean equals(Object obj) {
if(obj == this)
return true;
if(!(obj instanceof Address))
return false;
Address address = (Address)obj;
return address.getProvince().equals(province) && address.getCity().equals(city);
}

第8行从标明如果两个Address的 province 和 city 相同,那这两个Address就是相同的,这样equals方法就会回来true了。(不考虑字符串大小写问题)
掩盖完了equals(),接下来就是 掩盖hashCode()了。掩盖hashCode()的方针是:
如果两个目标 address1.equals(address2) 回来 false,那么 address1.hashCode() 最好 不等于 address2.hashCode()
当然,没有超级完美的hashCode(),如果相等了,那么当 hashMap.put(address1,value1)  hashMap.put(address2,value2) 就会put到同一个 hashmap的同一个槽下了。
 
重写了equals和hashCode的Address类如下:
public class Address {
    private String province;
    private String city;
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }  
    public Address() {}
    public Address(String province, String city) {this.province = province; this.city = city;}
    
    @Override
    public boolean equals(Object obj) {
        if(obj == this)
            return true;
        if(!(obj instanceof Address))
            return false;
        Address address = (Address)obj;
        return address.getProvince().equals(province) && address.getCity().equals(city);
    }
    @Override
    public int hashCode() {
        int result = 17;
        result += 31 * province.hashCode();
        result += 31 * city.hashCode();
        return result;
    }
}
测试类如下:
import java.util.HashMap;import java.util.Map;
public class TestAddress {  
    public static void main(String[] args) {
        Address address1 = new Address("广东","广州");
        Address address2 = new Address("广东", "广州");
        System.out.println(address1 == address2);//false
        System.out.println(address1.equals(address2));//true
        System.out.println(address1.hashCode() == address2.hashCode());//true        
        Address diff1 = new Address("四川","成都");
        Address diff2 = new Address("四川","绵阳");
        System.out.println(diff1 == diff2);//false
        System.out.println(diff1.equals(diff2));//false
        System.out.println(diff1.hashCode() == diff2.hashCode());//false        
        Map<Address, Integer> hashMap = new HashMap<Address, Integer>();
        hashMap.put(address1, 1);
        hashMap.put(address2, 2);//address2的hashCode 和 address1 相同,因此 put 方法会覆盖 address1 对应的 Value值1
        System.out.println(hashMap.get(address1));//2
        System.out.println(hashMap.get(address2));//2        
        hashMap.put(diff1, 1);
        hashMap.put(diff2, 2);
        System.out.println(hashMap.get(diff1));//1
        System.out.println(hashMap.get(diff2));//2    }

最后,其实Eclipse里面为我们提供了自动 生成 equals和hashCode的方法,可参考:JAVA中equals方法与hashCode方法学习。自动生成的方法如下:(还是自动生成的更专业呀。。。。)
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((city == null) ? 0 : city.hashCode());
        result = prime * result + ((province == null) ? 0 : province.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Address other = (Address) obj;
        if (city == null) {
            if (other.city != null)
                return false;
        } else if (!city.equals(other.city))
            return false;
        if (province == null) {
            if (other.province != null)
                return false;
        } else if (!province.equals(other.province))
            return false;
        return true;
    }