Java在我的“equals”实现中做了什么?

时间:2010-12-14 13:02:29

标签: java performance equals instanceof microbenchmark

今天,我偶然发现了以下情况:

考虑两个类NewClass和NewClass1,它们具有以下“equals”-methods:

的NewClass:

@Override
public boolean equals(Object obj) {
    return false;
}

public boolean equals(NewClass obj) {
    return value == obj.getValue();
}

NewClass1:

@Override
public boolean equals(Object obj) {
    if(!(obj instanceof NewClass1)) {
        return false;
    }
    return equals((NewClass1) obj);
}

public boolean equals(NewClass1 obj) {
    return value == obj.getValue();
}

我觉得奇怪的是,NewClass1中的等号似乎比NewClass中的等号慢得多(对于10.000.000调用14ms而不是3000ms)。起初,我认为这与“instanceof”检查有关,但如果我替换“return equals((NewClass1)obj);”与“返回假”;在NewClass1中,突然它的运行速度或多或少都相同。我真的不明白这里发生了什么,因为在我看来,equals(Object)中的return语句永远不会被实际调用。我在这里弄错了什么?

以下是我的“基准代码”,如果我在那里犯了一些错误:

public static void main(String[] args) {
    // TODO code application logic here

    NewClass i1 = new NewClass(1);
    NewClass i2 = new NewClass(1);
    NewClass i3 = new NewClass(5);

    NewClass1 j1 = new NewClass1(1);
    NewClass1 j2 = new NewClass1(1);
    NewClass1 j3 = new NewClass1(5);

    Object o1 = new Object();
    Object o2 = new Object();


    assert(i1.equals(i1));
    assert(i1.equals(i2));
    assert(i1.equals(i3) == false);
    assert(i1.equals(o1) == false);

    assert(j1.equals(j1));
    assert(j1.equals(j2));
    assert(j1.equals(j3) == false);
    assert(j1.equals(o1) == false);


    long start = System.currentTimeMillis();

    for(int i=0; i<1000000000; i++) {
        i1.equals(i1);
        i1.equals(i2);
        i1.equals(o1);
        i1.equals(o2);
    }

    long end = System.currentTimeMillis();

    System.out.println("Execution time was "+(end-start)+" ms.");



    start = System.currentTimeMillis();

    for(int i=0; i<1000000000; i++) {
        j1.equals(j1);
        j1.equals(j2);
        j1.equals(o1);
        j1.equals(o2);
    }

    end = System.currentTimeMillis();

    System.out.println("Execution time was "+(end-start)+" ms.");
}

4 个答案:

答案 0 :(得分:3)

我猜测正是消耗时间的测试实例。当您将该方法中的最终返回值更改为始终返回false时,编译器可能会删除条件,因为无论其评估结果如何,结果都将相同(返回false)。这也可以解释为什么更改最终返回会产生任何影响,因为正如您所说,它应该永远不会在代码路径中实现。

更一般地说,代码更改可能会影响性能,即使它不在执行的代码路径上,也可以通过更改编译器优化代码的方式来实现。

答案 1 :(得分:2)

在第一个例子中,equals(NewClass)通常永远不会被调用。 equals(Object)可以通过HotSpot(或类似内容)内联,并且您的测试主体可以简化为无效。

封套计算的后面可以提供信息。 “10.000.000调用8ms”是每秒1,250,000,000次迭代。假设一个4 GHz处理器,每次迭代大约需要三个周期。做一些有价值的事情有点快。事实上,代码说1,000,000,000而不是10,000,000。

实际上在实际代码中可以消除所有循环体。所以,你测量的东西并不重要 - 它不会是任何有用的可靠指示。使用微基准测试还有许多其他问题,您可以在许多其他地方阅读。

答案 2 :(得分:1)

在第一个示例中,您始终返回false。这非常快。在第二个示例中,您有一个更长的比较算法

答案 3 :(得分:0)

嗯,第一个示例几乎没有任何内容..您可以将迭代次数减少到100000,再次得到相同的结果,5或6 ms。这意味着JVM会积极地优化您的部分代码。