Java:if-return-if-return vs if-return-elseif-return

时间:2011-04-14 12:46:14

标签: java if-statement micro-optimization

unrelated question我的代码是这样的:

public boolean equals(Object obj)
{
    if (this == obj)
        return true;

    if (obj == null)
        return false;

    if (getClass() != obj.getClass())
        return false;

    // Check property values
}

我得到了一条评论,声称这不是最优的,相反(如果我理解正确的话)应该这样做:

public boolean equals(Object obj)
{
    if (this == obj)
        return true;

    else if (obj == null)
        return false;

    else if (getClass() != obj.getClass())
        return false;

    // Check property values
}

由于返回语句,我无法真正理解为什么它们中的任何一个应该比另一个更有效或更快。鉴于某个对象,就我所见,这两种方法都必须进行相同数量的检查。而且由于返回语句,任何一个都不会运行额外的代码。

我在这里遗漏了什么吗?有东西吗?是否有一些编译器优化或正在发生的事情或其他什么?

我知道这是微优化,我很可能坚持第一种方式,因为我觉得它看起来比较清晰,所有的ifs都在同一个位置。但我无能为力;我很好奇!

5 个答案:

答案 0 :(得分:22)

生成的字节代码对于这两种情况是相同的,所以它纯粹是一种风格问题。

我制作了两个方法e1e2,并且都生成了这个字节代码(使用javap -v读取):

public boolean e1(java.lang.Object);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_0
   1:   aload_1
   2:   if_acmpne   7
   5:   iconst_1
   6:   ireturn
   7:   aload_1
   8:   ifnonnull   13
   11:  iconst_0
   12:  ireturn
   13:  aload_0
   14:  invokevirtual   #25; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  aload_1
   18:  invokevirtual   #25; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   21:  if_acmpeq   26
   24:  iconst_0
   25:  ireturn

我遗漏了我之后编写的代码以使其编译。

答案 1 :(得分:9)

两者都没有比另一个更有效率。编译器可以很容易地看到两者是相同的,事实上,Suns / Oracles javac为这两种方法生成相同的字节码

这是一个IfTest类:

class IfTest {

    public boolean eq1(Object obj) {
        if (this == obj)
            return true;

        if (obj == null)
            return false;

        if (getClass() != obj.getClass())
            return false;

        return true;
    }


    public boolean eq2(Object obj) {

        if (this == obj)
            return true;

        else if (obj == null)
            return false;

        else if (getClass() != obj.getClass())
            return false;

        return true;
    }
}

我用javac编译它,反汇编如下:

public boolean eq1(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   if_acmpne   7
   5:   iconst_1
   6:   ireturn
   7:   aload_1
   8:   ifnonnull   13
   11:  iconst_0
   12:  ireturn
   13:  aload_0
   14:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   17:  aload_1
   18:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   21:  if_acmpeq   26
   24:  iconst_0
   25:  ireturn
   26:  iconst_1
   27:  ireturn

public boolean eq2(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   if_acmpne   7
   5:   iconst_1
   6:   ireturn
   7:   aload_1
   8:   ifnonnull   13
   11:  iconst_0
   12:  ireturn
   13:  aload_0
   14:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   17:  aload_1
   18:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   21:  if_acmpeq   26
   24:  iconst_0
   25:  ireturn
   26:  iconst_1
   27:  ireturn

也就是说,我建议使用第一个版本(没有else)。有些人可能会认为更干净其他部分,但我认为相反。 包含 else表示程序员没有意识到这是不必要的。

答案 2 :(得分:5)

我认为没有任何实际理由可以用任何方向替换其中一个实现 - 无论如何。

如果你想在一种方法中避免多个return语句,那么第二个例子就有意义了 - 有些人更喜欢这种编码方式。 然后我们需要if-else if构造:

public boolean equals(Object obj)
{
    boolean result = true;

    if (this == obj)
        result = true;

    else if (obj == null)
        result = false;

    else if (getClass() != obj.getClass())
        result = false;

    return result;
}

答案 3 :(得分:3)

这样想。当执行return语句时,控制会离开方法,因此else并没有真正添加任何值,除非你想说它增加了可读性(我不认为这样做,但其他人可能不同意)。

所以当你有:

if (someCondition)
    return 42;

if (anotherCondition)
    return 43;

向第二个else添加if没有任何价值。

事实上,我在编写名为Resharper的C#代码时使用了一个工具,它实际上会在这些情况下将else标记为无用的代码。所以我认为通常情况下,最好不要将它们排除在外。正如Joachim已经提到的那样,编译器无论如何都会优化它们。

答案 4 :(得分:0)

我认为这段代码可以稍微改进一点(请注意,非常可读):

  if (obj == null)
        return false;

  if (getClass() != obj.getClass())
        return false;

instanceof运算符相当于两者的组合,可能更快 - 更少的代码,没有方法调用:

  if (!(obj instanceof MyClass))
        return false;

但我知道什么......我懒得分析字节码(之前从未做过)。 :-P