在每个覆盖了equals方法的类中,必须覆盖hashCode方法,否则导致该类无法结合基于散列的集合一起正常工作,这些集合包括HashMap,HashSet,HashTable(废弃)

Object规范:1,在应用程序执行期间,只要对象的equals方法比较的操作用到的信息没有被修改,那么对同一个对象调用多次HashCode方法都必须始终返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的的整数可以不一致。

2,如果两个对象根据equals方法比较是相等的,那么调用两个对象的中任意一个对象的hashCode方法都必须产生相同的整数结果。

3,如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个hashCode方法,则不一定要产生相同的整数结果。但是应该知道,给不相同的对象产生截然不同的整数结果,有可能提高散列表的性能。

根据类的equals方法,两个截然不同的实例在逻辑上有可能是相等的但是根据Object类的HashCode方法,他们仅仅是两个没有任何相同之处的对象。因此对象的hashCode方法返回两个看起来是随机的整数,而不是根据第二个约定所要求的那样,返回两个相等的整数。

为不相等的对象产生不相等的散列码

/**
 * @Description
 * @Author liam661
 * @Date 2021/4/19 21:30
 **/
public final class PhoneNumber {
    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(int areaCode,int prefix, int lineNumber){
        rangeCheck(areaCode,999,"area code");
        rangeCheck(prefix,999,"prefix");
        rangeCheck(lineNumber,9999,"line number");
        this.areaCode = (short) areaCode;
        this.prefix = (short) prefix;
        this.lineNumber = (short) lineNumber;
    }
    private static void rangeCheck(int arg,int max,String name){
        if(arg < 0 || arg > max ){
            throw new IllegalArgumentException(name +": "+ arg);
        }
    }
    @Override
    public boolean equals(Object o){
        if(o == this)
            return true;
        if(!(o instanceof  PhoneNumber)){
            return false;
        }
        PhoneNumber pn = (PhoneNumber) o;
        return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;
    }
    public static void main(String [] args){
        PhoneNumber p1 = new PhoneNumber(77,867,5309);
        PhoneNumber p2 = new PhoneNumber(77,867,5309);
        Map<PhoneNumber,String> m = new HashMap<PhoneNumber,String>();
        m.put(p1,"jeeny");
        m.put(p2,"lining");
        Set<PhoneNumber> set = new HashSet<PhoneNumber>();
        set.add(p1);
        set.add(p2);
        System.out.println(m.get(p1));
        System.out.println(m.get(p2));
        System.out.println(set);
    }
}
null
[liam.mon4.PhoneNumber@2437c6dc, liam.mon4.PhoneNumber@6ed3ef1]

由于PhoneNumber类覆盖hashCode方法,从而导致两个相同的实例具有不相同的散列码,违反了hashCode的约定

 @Override
    public int hashCode(){
        int result = 17;
        result = 31 * result + areaCode;
        result = 31 * result + prefix;
        result = 31 * result + lineNumber;
        return result;
    }
lining
[liam.mon4.PhoneNumber@95916]

如果一个类是不变的,并且计算散列码的开销也很大,就应该考虑把散列码缓存在对象内部,而不是每次请求的时候都重新计算散列码。

@Override
    private volatile int hashCode;
    public int hashCode(){
        int result = hashCode;
        if(result == 0){
            result = 31 * result + areaCode;
            result = 31 * result + prefix;
            result = 31 * result + lineNumber;
            hashCode = result;
        }
        return result;
    }

但是不能产生最新的散列函数。

不要试图从散列码计算中排除掉一个对象的的关键部分来提高性能。

摘抄自《effective java 2》

 posted on 2021-04-19 22:18  岂曰-无衣  阅读(70)  评论(0编辑  收藏  举报