使用flag或if子句更有效吗?

时间:2013-10-04 16:47:08

标签: java performance if-statement boolean

在Java循环中,使用boolean标志而不是if语句更有效吗?

看看这两段代码。

使用旗帜:

public boolean isSomethingForAnyone() {
    boolean flag = false;
    for (Item item : listOfItems) {
        flag = flag || item.isSomething();
    }
    return flag;
}

使用if声明:

public boolean isSomethingForAnyone() {
    for (Item item : listOfItems) {
        if (item.isSomething())
            return true;
    }
    return false;
}

如果if在第一次迭代时返回isSomething(),那么带有true语句的方法当然会更快。但是,平均来说它更快还是分支减慢到足够慢以至于它更慢?另外,如果循环更快,情况会有所不同吗?为了简单起见,我在这里使用了for-each循环,我认为这比使用计数器迭代数组要慢。

3 个答案:

答案 0 :(得分:4)

这两段代码并不完全相同。

即使您只需要根据需要多次调用item.isSomething()(与我的原始答案相反),第一个版本仍会继续尝试迭代其余的集合。

想象一下Item.isSomething()的一个实现,它修改了找到该项的集合(如果它返回true)。那时,第一段代码会抛出一个ConcurrentModificationException,假设它是一个“常规”集合 - 而第二段代码只返回true

从根本上说,第二段代码更有效:它只迭代确定答案所需的列表,而不是通过所有内容。很可能性能不同是无关紧要的 - 特别是如果集合很小 - 但这取决于上下文。

您发现更多可读是另一回事 - 效率可能不会很高,尽管这取决于上下文。就个人而言,我发现第二个版本更易读以及更高效,所以我总是使用它。 (好吧,我会在if语句的主体周围添加大括号,但就是这样。)

答案 1 :(得分:0)

你这个循环字面上十亿次?如果没有,差异可能无法衡量。

你可以查看生成的字节码,看看究竟发生了什么,但编译器和jit以及vm本身可能会优化掉任何差异。

答案 2 :(得分:0)

如果您想知道机器上哪个“更快”,请运行它。

如果我们想知道哪些需要执行更多指令,那么我们可以查看字节码。

对于第一种方法,我们得到了这个(调用javap -c

Code:
   0: iconst_0      
   1: istore_1      
   2: getstatic     #2                  // Field listOfItems:Ljava/util/List;
   5: invokeinterface #3,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
  10: astore_2      
  11: aload_2       
  12: invokeinterface #4,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
  17: ifeq          50
  20: aload_2
  21: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
  26: checkcast     #6                  // class Item
  29: astore_3      
  30: iload_1       
  31: ifne          41
  34: aload_3       
  35: invokevirtual #7                  // Method Item.isSomething:()Z
  38: ifeq          45
  41: iconst_1      
  42: goto          46
  45: iconst_0      
  46: istore_1      
  47: goto          11
  50: iload_1       
  51: ireturn   

我们对循环的内部感兴趣,即第29-46行(第11-26行是迭代器的东西)。所以大约有10条指令。

对于第二种方法,我们得到了这个:

 Code:
       0: getstatic     #2                  // Field listOfItems:Ljava/util/List;
       3: invokeinterface #3,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
       8: astore_1      
       9: aload_1       
      10: invokeinterface #4,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
      15: ifeq          40
      18: aload_1       
      19: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      24: checkcast     #6                  // class Item
      27: astore_2      
      28: aload_2       
      29: invokevirtual #7                  // Method Item.isSomething:()Z
      32: ifeq          37
      35: iconst_1      
      36: ireturn       
      37: goto          9
      40: iconst_0      
      41: ireturn       

感兴趣的线路是27-37。所以7条指令。

从数字的角度来看,第二种方法是最重要的(请注意,我们假设所有堆栈操作都需要相同的时间来执行)。