Java重写hashCode和equals的一般约定

Effictive Java中第8、9条关于重写eqauls和hashCode的一般通用约定。

一、 重写 equals的一般约定

  • 自反性,对于任何非null引用x,x.equals(x)必须返回为true;
  • 对称性,对于任何非null引用x和y,当且仅当y.equals返回为ture时,x.equals(x)必须返回为true;
  • 传递性,对于任何非null引用x、y和z,如果x.equals(y)返回为true,并且y.equals(z),那么x.equals(z)也必须返回true;
  • 一致性,对于任何非null引用x和y,只要eqauls的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true,或者一致的返回false;
  • 对于任何非null值引用x,x.equals(null)必须返回false。

二、 重写hashCode一些技巧

  • i. 如果域是boolean型,则计算 (f ? 1 : 0)
  • ii. 如果域是byte、char、short或者int型,则计算 int(f)
  • iii. 如果域是long型,则计算 (int)(f ^ (f >>> 32))
  • iv. 如果域是float型,则计算Float.floatToIntBits(f)
  • v. 如果域是double型,则计算Double.doubleToIntBits(f),然后按照步骤 iii,为得到的long型值计算散列值
  • vi. 如果该域是一个对象引用,并且该类的equals方法通过递归地调用equals的方式来比较这个域。则同样为这个域递归地调用hashCode。如果需要更复杂的比较,则为这个域计算一个"范式",然后针对这个范式调用hashCode。如果这个域的值为null,这返回0。
  • vii. 如果这个域是一个数组,则要把每一个元素当作单独的域来处理。也就是说,递归地应用上述规则, 对每一个元素计算一个散列码,然后根据下面的方式把散列值组合起来。如果数组域的每个元素都很重要可以利用Arrays.hashCode的方法。

三、 Sample

/**
 * @version 1.0
 * @description Complex 复数对象
 * @date 2019-09-06
 * <p>
 * Effective java 重写hashCode equals标准写法
 */
public class Complex {
    private final double re;
    private final double im;

    public static final Complex ZERO = new Complex(0, 0);
    public static final Complex ONE = new Complex(1, 0);
    public static final Complex I = new Complex(0, 1);

    private Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    /**
     * @param re 实数域
     * @param im 虚数域
     * @return 一个复数(Complex)对象
     */
    public static Complex valueOf(double re, double im) {
        return new Complex(re, im);
    }

    /**
     * 极坐标形式的复数
     *
     * @param r     极坐标半径
     * @param theta 极坐标角度
     */
    public static Complex valueOfPalor(double r, double theta) {
        return new Complex(r * Math.cos(theta), r * Math.cos(theta));
    }

    public double realPart() {
        return re;
    }

    public double imaginaryPart() {
        return im;
    }

    public Complex add(Complex c) {
        return new Complex(re + c.re, im + c.im);
    }

    public Complex substract(Complex c) {
        return new Complex(re - c.re, im - c.im);
    }

    public Complex multiply(Complex c) {
        return new Complex(re * c.re - im * c.im, re * c.re + im * c.im);
    }

    public Complex divide(Complex c) {
        double tmp = c.re * c.re + c.im * c.im;
        return new Complex(re * c.re + im * c.im / tmp, re * c.re - im * c.im / tmp);
    }

    /**
     * hashCode 计算方式
     * i.    如果域是boolean型,则计算 (f ? 1 : 0)
     * ii.   如果域是byte、char、short或者int型,则计算 int(f)
     * iii.  如果域是long型,则计算 (int)(f ^ (f >>> 32))
     * iv.   如果域是float型,则计算 Float.floatToIntBits(f)
     * v.    如果域是Double型,则计算 Double.doubleToIntBits(f),然后按照步骤 iii,为得到的long型值计算散列值
     * vi.   如果该域是一个对象引用,并且该类的equals方法通过递归地调用equals的方式来比较这个域。
     * 则同样为这个域递归地调用hashCode。如果需要更复杂的比较,则为这个域计算一个"范式",
     * 然后针对这个范式调用hashCode。如果这个域的值为null,这返回0。
     * <p>
     * vii.  如果这个域是一个数组,则要把每一个元素当作单独的域来处理。也就是说,递归地应用上述规则,
     * 对每一个元素计算一个散列码,然后根据下面的方式把散列值组合起来。如果数组域的每个元素都很重要
     * 可以利用Arrays.hashCode的方法。
     * <p>
     * <p>
     * example:
     * <p>
     * int i;
     * long l;
     * float f;
     * double d;
     * int[] array;
     * <p>
     * int hashCode = 17 + i;
     * hashCode = 31 * hashCode + (int)(l ^ (l >>> 32));
     * hashCode = 31 * hashCode + Float.floatToIntBits(f);
     * hashCode = 31 * hashCode + (int)(Double.doubleToIntBits(d) ^ (Double.doubleToIntBits(d) >>> 32));
     * hashCode = 31 * hashCode + Arrays.hashCode(array);
     * <p>
     * 通常选用一个素数(17)作为开始,31做乘法。VM可以把 31 * i 优化成 (i << 5) - i
     */
    @Override
    public int hashCode() {
        int result = 17 + hashDouble(re);
        result = 31 * result + hashDouble(im);
        return result;
    }

    private int hashDouble(double val) {
        long longBits = Double.doubleToLongBits(val);
        return (int) (longBits ^ (longBits >>> 32));
    }

    /**
     * 重写equals 需要遵守以下协定:
     * 需要满足自反性,对称性,传递性,一致性。
     * <p>
     * 不要在equals里面做太多"智能"的事情
     */
    @Override
    public boolean equals(Object o) {
        // 1 引用地址判断
        if (o == this) {
            return true;
        }

        // instanceof 判断
        if (!(o instanceof Complex)) {
            return false;
        }

        // 其他判断
        Complex c = (Complex) o;
        return Double.compare(re, c.re) == 0 && Double.compare(im, c.im) == 0;
    }

    /**
     * 尽量为每一个类重写toString方式
     */
    @Override
    public String toString() {
        return "(" + re + " ," + im + ")";
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值