提供equals()的替代方案?

时间:2012-11-20 18:26:40

标签: java

假设我有一个名为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?");
        }
    }
}

5 个答案:

答案 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是“对象”级别的比较。它只是检查这两个对象是否引用同一个对象。

为特定类别编写“方程式方法”总是更好。例如,在这种情况下,最佳比较只是==