您可以將Double包裝在另一個類中,該類爲equals方法提供了「足夠接近」的方面。
package com.michaelt.so.doub;
import java.util.HashSet;
import java.util.Set;
public class CloseEnough {
private Double d;
protected long masked;
protected Set<Long> similar;
public CloseEnough(Double d) {
this.d = d;
long bits = Double.doubleToLongBits(d);
similar = new HashSet<Long>();
masked = bits & 0xFFFFFFFFFFFFFFF8L; // 111...1000
similar.add(bits);
similar.add(bits + 1);
similar.add(bits - 1);
}
Double getD() {
return d;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CloseEnough)) {
return false;
}
CloseEnough that = (CloseEnough) o;
for(Long bits : this.similar) {
if(that.similar.contains(bits)) { return true; }
}
return false;
}
@Override
public int hashCode() {
return (int) (masked^(masked >>> 32));
}
}
然後一些代碼來說明吧:
package com.michaelt.so.doub;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<CloseEnough> foo = new ArrayList<CloseEnough>();
foo.add(new CloseEnough(1.38));
foo.add(new CloseEnough(0.02));
foo.add(new CloseEnough(1.40));
foo.add(new CloseEnough(0.20));
System.out.println(foo.contains(new CloseEnough(0.0)));
System.out.println(foo.contains(new CloseEnough(1.37 + 0.01)));
System.out.println(foo.contains(new CloseEnough(0.01 + 0.01)));
System.out.println(foo.contains(new CloseEnough(1.39 + 0.01)));
System.out.println(foo.contains(new CloseEnough(0.19 + 0.01)));
}
}
這段代碼的輸出是:
false
true
true
true
true
(即第一個錯誤是與0的比較,只是爲了顯示它沒有找到那些不存在的東西)
CloseEnough只是一個簡單的封裝, ks是散列碼的最低三位(足夠的並且還將一組有效的相似數字存儲在一組中。在進行等值比較時,它使用這些集合。如果兩個數字在其集合中包含共同元素,則兩個數字相等。這就是說,我相當肯定有一些值將會出現問題,a.equals(b)
爲真,a.hashCode() == b.hashCode()
爲錯誤,在邊緣條件下仍然會出現適當的位模式 - 這會使一些事情(如HashSet和HashMap)'不高興'(並且可能會在某個地方提出一個好問題。
可能是這更好的方法將改爲以延長ArrayList中,這樣的indexOf
方法處理的數字之間的相似性:
package com.michaelt.so.doub;
import java.util.ArrayList;
public class SimilarList extends ArrayList<Double> {
@Override
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < this.size(); i++) {
if (get(i) == null) {
return i;
}
}
} else {
for (int i = 0; i < this.size(); i++) {
if (almostEquals((Double)o, this.get(i))) {
return i;
}
}
}
return -1;
}
private boolean almostEquals(Double a, Double b) {
long abits = Double.doubleToLongBits(a);
long bbits = Double.doubleToLongBits(b);
// Handle +0 == -0
if((abits >> 63) != (bbits >> 63)) {
return a.equals(b);
}
long diff = Math.abs(abits - bbits);
if(diff <= 1) {
return true;
}
return false;
}
}
與此代碼的工作變得更容易一點(雙關語不打算):
package com.michaelt.so.doub;
import java.util.ArrayList;
public class ListTest {
public static void main(String[] args) {
ArrayList foo = new SimilarList();
foo.add(1.38);
foo.add(1.40);
foo.add(0.02);
foo.add(0.20);
System.out.println(foo.contains(0.0));
System.out.println(foo.contains(1.37 + 0.01));
System.out.println(foo.contains(1.39 + 0.01));
System.out.println(foo.contains(0.19 + 0.01));
System.out.println(foo.contains(0.01 + 0.01));
}
}
此代碼的輸出是:
false
true
true
true
true
在這種情況下,根據代碼HasMinimalDifference在相似列表中完成位擺動。同樣,數字被轉換爲比特,但這次數學是在比較中完成的,而不是試圖處理包裝器對象的相等和散列碼。
核心Java中沒有優雅的數學庫解決方案(我知道) - 您必須使用epsilon小於某個最小值的體面測試來推出自己的版本,或者使用第三方庫。說到這一點,我相信Apache庫有這方面的一些東西。 –
可能重複[使用==運算符比較float/double值](http://stackoverflow.com/questions/6837007/comparing-float-double-values-using-operator?rq=1) –
從@HovercraftFullOfEels之後,您可以簡單地編寫自己的方法來檢查列表是否包含通過循環收集的值。它將同樣有效,並且實施很簡單。 – Whymarrh