【Java基础】关于复数类的hashCode()重写的讨论

1.源码

public class Complex implements Cloneable{
    //实部虚部,复数单位
    public double real, imag;
    public final static char imag_unit='i';
    //其它常量(复平面上的无穷大与非数字们)
    public final static Complex NaN=new Complex(Double.NaN,Double.NaN);
    public final static Complex PN_INFINITY=new Complex(Double.POSITIVE_INFINITY,Double.NEGATIVE_INFINITY);
    public final static Complex PP_INFINITY=new Complex(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY);
    public final static Complex NN_INFINITY=new Complex(Double.NEGATIVE_INFINITY,Double.NEGATIVE_INFINITY);
    public final static Complex NP_INFINITY=new Complex(Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY);
    public final static Complex PZ_INFINITY=new Complex(Double.POSITIVE_INFINITY,0);
    public final static Complex NZ_INFINITY=new Complex(Double.NEGATIVE_INFINITY,0);
    public final static Complex ZP_INFINITY=new Complex(0,Double.POSITIVE_INFINITY);
    public final static Complex ZN_INFINITY=new Complex(0,Double.NEGATIVE_INFINITY);
    //构造方法
    public Complex(){}
    public Complex(double real, double imag){this.real=real; this.imag=imag;}
    //判等哈希
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Complex){
            Complex that=(Complex) obj;
            boolean is_real_same=(this.real==that.real);
            boolean is_imag_same=(this.imag==that.imag);
            boolean is_real_inf=(this.real==Double.POSITIVE_INFINITY||this.real==Double.POSITIVE_INFINITY);
            boolean is_imag_inf=(this.imag==Double.POSITIVE_INFINITY||this.imag==Double.POSITIVE_INFINITY);
            if(is_imag_inf && !is_real_inf)
                return is_imag_same;
            else if (!is_imag_inf && is_real_inf)
                return is_real_same;
            else
                return is_imag_same && is_real_same;
        }
        else throw new ClassCastException("For input object: "+obj);
    }
    @Override
    public int hashCode(){
        //抵御撞哈希攻击的能力比较弱,参考了Point类中的实现
        long r = Double.doubleToLongBits(real);
        r ^= Double.doubleToLongBits(imag)*31;
		return (((int) r) ^ ((int) (r >> 32)));
    }
    //克隆方法
    @Override
    public Object clone(){
        try {
            Complex that=(Complex)super.clone();
            that.real=this.real; that.imag=this.imag;
            return that;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
    //类型转换
    public static Complex parseComplex(Object obj) throws NumberFormatException{
        try{double real=Double.parseDouble(obj.toString()); return new Complex(real,0);}
        catch(ClassCastException|NumberFormatException e){throw new NumberFormatException("For input object: "+obj);}
    }
    @Override
    public String toString(){return real+" + "+imag+imag_unit;}
    //加减乘除
    public static Complex add(Complex v1, Complex v2){return new Complex(v1.real+v2.real,v1.imag+v2.imag);}
    public static Complex sub(Complex v1, Complex v2){return new Complex(v1.real-v2.real,v1.imag-v2.imag);}
    public static Complex mul(Complex v1, Complex v2){
        Complex res=new Complex();
        res.real=v1.real*v2.real-v1.imag*v2.imag;
        res.imag=v1.real*v2.imag+v1.imag*v2.real;
        return res;
    }
    public static Complex div(Complex v1, Complex v2){
        Complex res=new Complex();
        res.real=(v1.real*v2.real+v1.imag*v2.imag)/(v2.real*v2.real+v2.imag*v2.imag);
        res.imag=(v1.imag*v2.real-v1.real*v2.imag)/(v2.real*v2.real+v2.imag*v2.imag);
        return res;
    }
    public Complex add(Complex that){return add(this,that);}
    public Complex sub(Complex that){return sub(this,that);}
    public Complex mul(Complex that){return mul(this,that);}
    public Complex div(Complex that){return div(this,that);}
    //模长辐角
    public static double norm(Complex v1){return sqrt(v1.real*v1.real+v1.imag*v1.imag);}
    public static double arg(Complex v1){return atan(v1.imag/v1.real);}
    public double arg(){return arg(this);} public double norm(){return norm(this);}
}

相等判断和哈希这里想着重说一下

2.相等判断

判断相等需要考虑复平面上的无穷远点, 按照我们的封装, 复平面的无穷远点并非只有一个, 而且注意从数学意义上而言, (+∞,1)和(+∞,2)都是指模长为∞,幅角为0的点, 这两个点应该是相等的, 而且这里认定了不同方位的无穷远点是不同的点, 如果考虑其它的复平面定义, 可以将所有无穷远点判定相等, 判断相等的实现比较灵活.

3.哈希算法

关于hashCode(), 在网上看了一些实现, 有两种比较主流的做法, 一种是直接从Object那里继承, 另一种是调用toString()后返回得到的String的哈希值.
  第一种做法(继承Object的hashCode())显然不提倡, 我们希望两个复数对象, 只要它们从数学意义上保存了相同的数据, 就总能得到相同的哈希 (即哈希对同值数据应当是稳定的), 第一个做法从稳定性的角度来看就可以直接否掉了.
  第二种做法toString(), 它的问题隐蔽一些, 其主要问题是不满足哈希算法的均匀性, 即我们需要哈希码能够均匀的分配给类对象的所有状态 – 对于复数类, 类对象的所有状态共包含了 (264)2 种可能, 而哈希码的所有可能取值共有232种, 一个勉强及格的哈希算法至少需要保证: 每个哈希码被分配给(264)2÷232个可能状态(如果需要做到优秀, 则至少还要保证哈希码不容易被哈希碰撞攻击, 但对于java而言, 这是很难的)–这显然不能通过toString().hashCode()来实现. 另外, toString()涉及到调用其它方法,这会导致哈希速度变慢, 在可应用的项目中是十分致命的.
  一个正确(但不是十分优秀)的做法是参照了java.awt.geom.Point2D来实现的–参照它的理由就如同复数在复平面上的几何意义那般显而易见, 且具有良好的均匀性. 注意到虽然这里涉及到了大数乘法, 但被乘数是31, 这一点在jvm底层中是通过简化过的位运算实现的, 执行效率仍然比较高. 第1部分的源码中正是参考了这一实现.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值