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部分的源码中正是参考了这一实现.