我正在重写对象的相等方法。假设里程表将km变量存储为双精度(以及示例中其他不重要的其他变量)。
public class Odometer {
private double km;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(km);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Odometer other = (Odometer) obj;
if (Double.doubleToLongBits(km) != Double.doubleToLongBits(other.km))
return false;
return true;
}
}
现在,由Eclipse生成的double变量的比较(以及哈希码)是精确的按位比较。但是,有人告诉我在比较float或double值时使用“ epsilon”差异。我什至听说它的短语是“比较浮点数时不要使用相等性。”
boolean equals(double x, double y, double epsilon) {
return x - y < epsilon;
}
用于Doubles的JUnit assertEquals
方法证明了这一点:
assertEquals(double expected, double actual, double epsilon)
那么,我应该在这里使用哪个比较?
答案 0 :(得分:6)
equals
方法的Javadoc状态(强调我的意思):
https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-
equals方法对非null对象引用实现等效关系:
- 这是自反的:对于任何非空参考值x,x.equals(x)应该返回true。
- 这是对称的:对于x和y的任何非空参考值,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。
- 它是可传递的:对于任何非空参考值x,y和z,如果x.equals(y)返回true,而y.equals(z)返回true,则x.equals(z)应该返回true。
- 这是一致的:对于任何非空参考值x和y,只要未修改对象的equals比较中使用的信息,对x.equals(y)的多次调用将始终返回true或始终返回false。
- 对于任何非空参考值x,x.equals(null)应该返回false。
相等方法必须是可传递的。如果您使用了epsilon,则将无法保持。
考虑双精度值x = 2.0,y = 2.6,z = 3.1和epsilon = 1.0。
请注意,z-y = 0.5和y-x = 0.6,两者均小于1.0的ε。但是,z-x = 1.1,比多。
因此,我们将有“ x等于y”和“ y等于z”,但没有“ x等于z”,这破坏了传递性。如果这些是其他某些对象的实例变量,例如上面示例中的里程表,则会发生同样的情况。
因此,平等应该是精确的。如上使用Double.compare(double d1, double d2)
或将它们转换为Double值,然后使用Double.compareTo(Double anotherDouble)
一样,将其转换为位是可行的。请注意,它们会将0.0和-0.0视为不同的数字。
https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#compare-double-double- https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#compareTo-java.lang.Double-
这对于保持哈希函数的一致性也很重要。
即使对于原始双精度值,也不要使用内置的Java等式运算符==
。如JavaDocs中关于compareTo
方法所述,等于NaN
失败。 (此StackOverflow问题还提供了更多信息:Why is Java's Double.compare(double, double) implemented the way it is?)
最后一点-由于使用了原始的double值,因此不适用于上面的示例,但是如果您使用Double
对象,请记住在尝试将null
传递给任何对象之前先进行检查{{1}} Double比较功能。