正确实现equals() - 分数的方法

时间:2014-09-01 12:17:22

标签: java hashmap equals fractions

我希望printn方法给我“Asterix”和“Oberlix”,因为3/4与6/8相同。

HashMap hm = new HashMap();
hm.put(new Fraction(3, 4), "Asterix");
hm.put(new Fraction(19, 12), "Oberlix");
System.out.println(hm.get(new Fraction(6, 8)));
System.out.println(hm.get(new Fraction(38, 24)));

这就是我实现equals-Method的方法:

public boolean equals(Object obj) {
    boolean isEqual = false;

    if(obj instanceof Fraction) {
        Fraction frac = (Fraction) obj;

        if(((double) this.numerator / (double) this.denumerator) == ((double) frac.numerator / (double) frac.denumerator)) {
            isEqual = true;
        }
    }
    return isEqual;
}

显然我做错了,因为那不起作用,我的print方法返回“null”。我的想法是,如果我分配两个分数的分子和分子,结果必须相等,如果分数相等(3/4与6/8相同)。

对不起伙计们,我猜这个错误肯定是显而易见的,但我找不到它。

4 个答案:

答案 0 :(得分:2)

你可以做等于

return denominator * other.numerator == numerator * other.denominator;

但更好的是制作规范分数。 无论是在equals中还是在构造函数中,将分数标准化:6/8变为3/4。

public class Fraction implements Number {
    private final int numerator;
    private final int denominator;

    public Fraction(int numerator, int denominator) {
        if (denominator < 0) {
            denominator = -denominator;
            numberator = -numerator;
        }
        int commonFactor = gcd(numerator, denominator);
        this.numerator = numerator / commonFactor;
        this.denominator = denominator / commonFactor;
    }

    @Override
    public boolean equals(Object other) {
        ...
        Fraction otherFraction = ...
        return denominator == otherFraction.denominator
            && numerator == otherFraction.numerator;
    }

    private static int gcd(int x, int y) {
        x = Math.abs(x);
        y = Math.abs(y);
        ...
        while (x != y) {
            if (x > y) {
                x -= y;
            } else {
                y -= x;
            }
        }
        return x;
    }

什么更好?您现在可以创建一个hashCode:

@Override
int hashCode() {
    return denominator * 31 + numerator;
}

浮点是有限数量的2的近似和。

答案 1 :(得分:1)

要使HashMap生效,您需要同时实施equalshashCode。我只为equals提供部分答案,因为我没有太多时间。

要比较两个分数而不诉诸double,只需做一些简单的算术。您有两个分数a/bc/d。假设分母是非零的:

a/b == c/d
  (multiply left and right by b)
a == c/d*b
  (multiply left and right by d)
a*d == c*b

所以:

public boolean equals(Object obj) {
  if (!(obj instanceof Fraction)) {
    return false;
  }
  Fraction other = (Fraction) obj;
  return this.numerator * other.denominator == other.numerator * this.denominator;
}

请注意,对于非常大的分数,这不会起作用;他们会溢出来的。如果你想正确处理这些问题,请加长。


为了实现hashCode,您可以使用欧几里德算法简化分数,然后xor分子和分母的哈希码。

答案 2 :(得分:0)

您永远不应该将==与双倍进行比较,因为System.out.println(0.1+0.1+0.1)并不总是0.3(对我来说,它输出0.30000000000000004)。使用equals中的compareDouble方法。

因为您要在类Fraction中存储分子和去除者,所以您应该在equals方法中使用足够接近条件和自定义epsilon:

public boolean equals(Object obj) {
    boolean isEqual = false;

    if(obj instanceof Fraction) {
        Fraction frac = (Fraction) obj;

        if(Math.abs(((double)this.numerator)/this.denumerator) - ((double)frac.numerator)/frac.denumerator) < .00000001/*epsilon*/) {
            isEqual = true;
        }
    }
    return isEqual;
}

此外,您需要覆盖班级hashCode中的Fraction方法才能使用HashMap。由于此equals实现仅依赖于一个值(分数的结果),因此您可以使用以下内容:

public int hashCode()
{

    return 0;//all Fraction return the same hashCode, which make HashMap call equals each time

    //EDIT: the following is WRONG: assuming eps = 0.1, 299/100 is equals to 300/100 but hashCode will be different (2 and 3).

    //return this.numerator/this.denumerator ;//since those are int (I guess),
    //it will truncate the floating part. So you will just check for the integer part.


}

答案 3 :(得分:0)

作为上面的帖子,你解决方案的方法是使用&#34; hasCode()&#34;而不是等于()。
这是一个关于如何获得正确hashCode的选项:

    @Override
    public int hashCode() {
        // Calculating with double is troublesome sometimes, so i use BigDecimal here
        BigDecimal value = BigDecimal.valueOf(this.numerator).divide(BigDecimal.valueOf(this.denumerator), RoundingMode.HALF_UP);
        // after this, i just return the hashCode i would get, if if my parameter was a simple Double Value:
        return Double.valueOf(value.doubleValue()).hashCode();
    }

希望这有帮助!