Java 如何为包含双字段的类实现equals/hashCode方法

Java 如何为包含双字段的类实现equals/hashCode方法,java,Java,我在一个包含双字段的类中重写equals和hashCode。我的第一种方法是在equals方法中使用epsilon测试,在hashCode中使用Double.hashCode(Double),但这会导致相同的对象具有不同的hash代码;以下是一个简化的示例: public class DoubleHashTest2 { public static void main(String[] args) { double base1 = .9; do

我在一个包含双字段的类中重写equals和hashCode。我的第一种方法是在equals方法中使用epsilon测试,在hashCode中使用Double.hashCode(Double),但这会导致相同的对象具有不同的hash代码;以下是一个简化的示例:

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;
        }
    }
}
但是选择一个好的取整算法是困难的,而且这似乎有点昂贵。我查看了类似的类,例如Point2D.Double,但是这个类中的equals失败了,例如,当比较.8和0.7999999999时

是否有处理此问题的推荐方法?

回答主要问题 您不需要任何自定义舍入,因为
Double
类有一个方法,它将
Double
转换为
long
(两者都是64位值)

另外,对于
equals()
方法,可以将两个
double
值与进行比较

可能的
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
的缺点-即使具有相同大小的值也可以给出相近的结果,但结果不同。也许你应该用

另外,请参见答案。

这个怎么样

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;
}
@覆盖
公共int hashCode(){
返回Double.hashCode(Math.round(dnum1));
}
公共布尔等于(对象其他)
{
if(其他测试实例)
返回等于(测试);
返回false;
}
公共布尔等于(测试其他)
{
如果(其他==null)
返回false;
返回相等(dnum1,其他.dnum1);
}
公共布尔值相等(双倍数,双倍另一个数){
如果(number==anotherNumber)//例如0,NaN,+/-无穷大
返回true;

return Math.abs(number-anotherNumber)Yes:放弃双相等;如果希望匹配的结果不是100%完全匹配,请停止使用equals和hashCode。任何尝试都将失败。您的方法定义
equals(Test)
不正确。equals必须作为
equals(Object)从
对象重写
不幸的是,Double.compare(.9-.1,.7+.1))测试不相等;返回1。doubleToLongBits为两个表达式返回不同的值。这是正确的,因为
(.9-.1)=0.8
(.7+.1)=0.7999999999999
。它们显然是不相等的。我认为您试图以错误的方式解决这个问题-如果您需要浮点运算,也许您应该使用?当然,如果需要,您可以稍后使用
doubleValue()将其转换为
double
。谢谢。BigDecimal将是解决此问题的合适方法。如果我正在编写图形应用程序,我不想使用BigDecimal,我仍然希望将位置(1.0,1.0)视为等于位置(.999999999999999,.99999999999999999)
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
}
@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;
}