假设我有一个名为Number
的类,我打算对Number
个对象进行大量的等式比较。我担心泛型Number::equals(Object o)
方法的“开销”(类比较等)。在这种情况下,提供诸如Number::isEqualTo(Number other)
之类的方法作为Number::equals(Object o)
的替代方案是否有用?这是一种常见的模式吗?或者JVM目前是否已经足够好地优化,这样做没有优势呢?
这是一个代码示例:
public class Number {
int _value;
Number(int value) {
_value = value;
}
@Override
public boolean equals(final Object o) {
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != getClass()) return false;
return isEqualTo((Number)o);
}
public boolean isEqualTo(final Number other) {
return _value == other._value;
}
public static void main(String[] args) {
Number one = new Number(1);
Number two = new Number(2);
if (!one.isEqualTo(two)) {
System.out.println("fast comparison?");
}
if (!one.equals(two)) {
System.out.println("slow comparison?");
}
}
}
答案 0 :(得分:5)
这两种方法有不同的语义:
equals
具有Object::equals
合同规定的语义,而isEqualTo
具有仅适用于Number
个对象由于比较不是苹果对苹果,因此equals
需要更多CPU周期是公平的。但是,你不太可能注意到这种差异。
像你这样的类实现Comparable<T>
更为常见。语义在那里需要进行排序检查,而不仅仅是对等式检查,但不要求使用未知类的对象,这样可以节省CPU周期。
你应该有充分的理由提供平等的替代方案(例如,指向equals(Object)
的探查器运行作为瓶颈,由于更改而感知到的可读性改进,或者由于采用了更多的界面)。为了减少几个CPU周期而这样做是不成熟的。
答案 1 :(得分:3)
具有最不利方案的快速微基准测试(equals
始终调用isEqualTo
)显示(以毫秒为单位):
等于:1014
isEqualTo:1010
底线:除非您的程序没有做任何其他事情,否则这不会成为性能瓶颈,您应该坚持优化的第一个原则:首先配置文件,然后优化需要优化的内容。
测试代码:
public class TestPerf {
private static int NUM_RUN;
private static List<Number> list = new ArrayList<>();
public static void main(String[] args) {
NUM_RUN = 100_000;
for (int i = 0; i < 10000; i++) {
list.add(new Number(i));
}
long sum = 0;
System.out.println("Warmup");
for (int i = 0; i < NUM_RUN; i++) {
sum += method1(17);
sum += method2(17);
}
System.gc();
System.out.println("Starting");
sum = 0;
long start = System.nanoTime();
for (int i = 0; i < NUM_RUN; i++) {
sum += method1(17);
}
long end = System.nanoTime();
System.out.println("equals: " + (end - start) / 1000000);
System.gc();
start = System.nanoTime();
for (int i = 0; i < NUM_RUN; i++) {
sum += method2(17);
}
end = System.nanoTime();
System.out.println("isEqualTo: " + (end - start) / 1000000);
System.out.println(sum);
}
private static int method1(int target) {
int sum = 0;
Number comparison = new Number(target);
for (Number n : list) {
if (n.equals(comparison)) sum++;
}
return sum;
}
private static int method2(int target) {
int sum = 0;
Number comparison = new Number(target);
for (Number n : list) {
if (n.isEqualTo(comparison)) sum++;
}
return sum;
}
public static class Number {
int _value;
Number(int value) {
_value = value;
}
@Override
public boolean equals(final Object o) {
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != getClass()) return false;
return isEqualTo((Number) o);
}
public boolean isEqualTo(final Number other) {
return _value == other._value;
}
}
}
答案 2 :(得分:1)
这取决于您希望使用比较方法的位置。
也许你可以使用Comparator接口的不同实现?
这些可以用于例如。排序列表。
答案 3 :(得分:1)
您甚至可以提供equals
本身的重载:equals(Number)
。如果您实施非常小心(与equals(Object)
无法区分),您可以通过在某些情况下避免检查向下转换来实现极小的加速。请注意,您仍然需要检查a.getClass() == b.getClass()
,因此差异非常小。
答案 4 :(得分:0)
xx.isEqualTo.yy
是“对象”级别的比较。它只是检查这两个对象是否引用同一个对象。
为特定类别编写“方程式方法”总是更好。例如,在这种情况下,最佳比较只是==
。