7

在 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语句的方法当然更快。但是,它是平均速度更快还是分支速度减慢到足以使其变慢?另外,如果循环更快,情况会有所不同吗?在这里,为了简单起见,我使用了 for-each 循环,我认为这比使用计数器遍历数组要慢。isSomething()true

4

3 回答 3

4

这两段代码并不完全等价。

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

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

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

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

于 2013-10-04T16:53:11.450 回答
0

你在做这个循环十亿次吗?如果没有,差异可能无法衡量。

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

于 2013-10-04T16:50:39.317 回答
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 条指令。

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

于 2013-10-04T16:59:28.310 回答