Effective Java 第三版读书笔记(对于所有对象都通用的方法2)

第11条.覆盖equals时总要覆盖hashCode

在每个覆盖equals方法的类中,都必须覆盖hashCode方法。否则可能会导致该类无法结合所有基于散列的集合一起正常运作(包括HashMap和HashSet),以下是约定的内容:

1)在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对同一个对象的多次调用,hashCode方法必须始终返回同一个值。在一个应用程序与另一个程序的执行过程中,执行hashCode方法所返回的值可以不一致。

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

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

因没有覆盖hashCode而违反的关键约定是第二条:相等的对象必须具有相等的散列码。因为两个截然不同的实例在逻辑上可能是相等的,但是根据Object类的hashCode方法,它们仅仅是两个没有任何共同之处的对象。因此,对象的hashCode方法返回两个看起来是随机的整数,而不是要求的相等的整数。

理想情况下,散列函数应该把集中不相等的实例均匀地分布到所有可能的int值上。可以根据以下方法相对接近理想情形:

1.a.为该域计算int类型的散列码c。

   1).如果该域是基本类型,则计算Type.hashCode(f)。

   2).如果该域是一个对象引用,并且该类的equals方法通过递归地调用equals的方式来比较这个域,则同样为这个域递归调用  hashCode.

   3).如果该域是一个数组,则要把每一个元素当做单独的域来处理。也就是说,递归地应用上述规则,对每个重要元素计算一个散列码,如果数组域中的所有元素都很重要,可以使用Arrays.hashCode方法。

   b.按照下面的公式,把a中计算得到的散列码c合并到result中.

   result = 31*result + c;     

package com.example.ownlearn;

public class PhoneNumber {
    private final int areaCode,prefix,lineNum;

    public PhoneNumber(int areaCode,int prefix,int lineNum){
        this.areaCode = areaCode;
        this.prefix = prefix;
        this.lineNum = lineNum;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == this)
            return true;
        PhoneNumber ph = (PhoneNumber) obj;
        return ph.prefix == prefix && ph.lineNum == lineNum && ph.areaCode == areaCode;
    }

    @Override
    public int hashCode() {
        int result = Integer.hashCode(areaCode);
        result = 31 * result + Integer.hashCode(prefix);
        result = 31 * result + Integer.hashCode(lineNum);
        return result;
    }
}

2.我们也可以采用Object类的静态方法,只不过运行速度更慢一些。

 @Override
    public int hashCode() {
        return Objects.hash(lineNum,prefix,areaCode);
    }

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

不要对hashCode方法的返回值做出具体的规定。

第12条.始终要覆盖toString

Object提供的toString方法的一个实现,返回的是类的名称@散列码的无符号十六进制表示。提供好的toString实现可以使类用起来更加舒适,使用了这个类的系统也更易于调试。

第13条.谨慎地覆盖clone

1.不可变的类永远都不应该提供clone方法。

2.必须确保它不会伤害到原始对象,并确保正确地创建被克隆对象中的约束条件。

package com.example.ownlearn;

import java.util.Arrays;
import java.util.EmptyStackException;

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack(){
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e){
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop(){
        if(size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null;
        return result;
    }

    private void ensureCapacity(){
        if(elements.length == size)
            elements = Arrays.copyOf(elements,2 * size +1);
    }

    @Override
    protected Stack clone() throws CloneNotSupportedException {
        Stack result = (Stack)super.clone();
        result.elements = elements.clone();
        return result;
    }
}

为了使Stack类中的clone方法正常工作,它必须要拷贝栈的内部信息。我们不需要将 elements.clone()的结果转换成Object[]。在数组上用clone返回的数组,其编译时的类型与被克隆数组的类型相同。复制功能最好由构造器或者工厂提供,但是数组例外,最好利用clone方法复制数组。

第14条.考虑实现Comparable接口

compareTo方法的通用约定:

符号sgn(expression)表示数学中的signum函数,它根据表达式的值为负值、零和正值,分别返回-1、0或1.

1.实现者必须确保所有的x和y都满足sgn(x.compareTo(y)) == -sgn(y.compareTo(x))

2.实现者必须确保这个比较关系是可传递的:(x.compareTo(y) >0 &&  y.compareTo(z)>0) 暗示着x.compareTo(z) >0

 

  private static final Comparator<PhoneNumber> COMPARATOR =
            Comparator.comparingInt((PhoneNumber pn) -> pn.areaCode)
                .thenComparingInt(pn -> pn.prefix)
                .thenComparingInt(pn -> pn.lineNum);

    public int compareTo(PhoneNumber pn){
        return COMPARATOR.compare(this,pn);
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值