我在包含双字段的类中重写equals和hashCode。我的第一种方法是在equals方法中使用epsilon测试,在hashCode中使用Double.hashCode(double),但这会导致具有不同哈希码的相等对象;这是一个简化的例子:
public class DoubleHashTest2
{
public static void main(String[] args)
{
double base1 = .9;
double base2 = .7;
Test test1 = new Test( base1 - .1 );
Test test2 = new Test( base2 + .1 );
System.out.println( test1.equals( test2 ) );
System.out.println( test1.hashCode() );
System.out.println( test2.hashCode() );
}
private static class Test
{
private double dnum1;
public Test( double dnum1 )
{
this.dnum1 = dnum1;
}
public boolean equals( Test other )
{
final double epsilon = .0001;
boolean result = false;
if ( this == other )
result = true;
else if ( other == null )
result = false;
else
result = Math.abs( this.dnum1 - other.dnum1 ) < epsilon;
return result;
}
public int hashCode()
{
int hash = Double.hashCode( dnum1 );
return hash;
}
}
}
我已经想过几种解决方案,包括转换为BigDecimal,但我对它们中的任何一种都不满意。我终于决定四舍五入了:
public boolean equals( Test other )
{
boolean result = false;
if ( this == other )
result = true;
else if ( other == null )
result = false;
else
{
double test1 = round( dnum1 );
double test2 = round( other.dnum1 );
result = test1 == test2;
}
return result;
}
public int hashCode()
{
double temp = round( dnum1 );
int hash = Double.hashCode( temp );
return hash;
}
private double round( double dnum )
{
// tests for NaN and +/-infinity omitted for brevity
final int places = 4;
final double round_const = Math.pow( 10, places );
double result = ((int)(dnum * round_const + .5)) / round_const;
return result;
}
但选择一个好的舍入算法很困难,这看起来很昂贵。我查看了类似的类,例如Point2D.Double,但是这个类中的equals失败了,例如,当比较.8和0.7999999999999999时。
是否有推荐的方法来处理此问题?
答案 0 :(得分:0)
您不需要任何自定义舍入,因为Double
类具有doubleToLongBits()
方法,只需将double
转换为long
(两者都是64位值)。
此外,对于equals()
方法,您可以将两个double
值与Double#compare()
进行比较。
您的示例可能equals()
和hashCode()
:
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (null == other
|| this.getClass() != other.getClass()) {
return false;
}
return Double.compare(this.dnum1, ((Test) other).dnum1) == 0;
}
public int hashCode() {
long bits = Double.doubleToLongBits(this.dnum1);
return (int) (bits ^ (bits >>> 32));
}
您的示例显示了double
用于浮点计算的缺点 - 即使具有相同幅度的值也可以给出接近但不同的结果。也许你应该使用BigDecimal?
另请参阅this question个答案。
答案 1 :(得分:0)
怎么样?
public static boolean equals( double param1, double param2 )
{
final double epsilon = 1e-10;
// accounts for 0, NaN, +/-INFINITY
boolean result = param1 == param2;
if ( !result )
{
double quot = param1 / param2;
result = quot > 0 && (1 - quot) < epsilon;
}
return result;
}
演示:
public static void main(String[] args)
{
double dvar1 = 0.7 + 0.1;
double dvar2 = 0.9 - 0.1;
System.out.println( dvar1 == dvar2 ); // expect false
System.out.println( equals( dvar1, dvar2 ) ); // expect true
}
答案 2 :(得分:0)
@Override
public int hashCode() {
return Double.hashCode(Math.round(dnum1));
}
public boolean equals( Object other )
{
if(other instanceof Test)
return equals(test);
return false;
}
public boolean equals( Test other )
{
if(other == null)
return false;
return isEqual(dnum1,other.dnum1);
}
public boolean isEqual(double number,double anotherNumber){
if (number == anotherNumber)// eg 0, NaN, +/-INFINITY
return true;
return Math.abs(number - anotherNumber) <= EPSILON;
}