[英]Correct implementation of the equals()-Method for 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;
}
顯然我做錯了,因為那不起作用,並且我的打印方法返回“ null”。 我的想法是,如果我分別使用兩個分數的分子和分子,那么如果分數相等(3/4與6/8相同),則結果必須相等。
抱歉,我想這個錯誤一定很明顯,但我找不到。
你可以做到平等
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的冪的近似和。
為了使HashMap
正常工作,您需要同時實現equals
和hashCode
。 我將提供部分答案,僅適用於equals
,因為我沒有太多時間。
要比較兩個分數而不求助於double
s,只需執行一些簡單的算法即可。 您有兩個分數a/b
和c/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
,您可以使用Euclidean算法簡化分數,然后對分子和分母的哈希碼進行異或運算。
您永遠不要將==
與double進行比較,因為System.out.println(0.1+0.1+0.1)
並不總是0.3
(對我來說,它輸出0.30000000000000004
)。 使用Double
equals
或compare
方法。
因為在類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;
}
同樣,您將需要在類Fraction
中重寫hashCode
方法,以使用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.
}
如上面的帖子所述,解決方案的方法是使用“ hasCode()”而不是equals()。
這是有關如何獲取正確的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();
}
希望這可以幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.