2014-05-04 43 views
6

下面两个代码片段在性能上有什么不同吗?这两个循环之间有什么区别吗?

for(String project : auth.getProjects()) { 
    // Do something with 'project' 
} 

String[] projects = auth.getProjects(); 
for(String project : projects) { 
    // Do something with 'project' 
} 

对于我来说,我觉得第二个是更好的,但它是更长的时间。第一个更短,但我不确定它是否更快。我不确定,但对我来说,似乎每次循环迭代时,都会调用auth.getProjects。那不是吗?

+0

它只被调用一次,但第二个版本太冗长了。你正在做的是通过一个列表。一条线就够了。 – Navin

回答

10

编辑@StephenC是对的,JLS是一个更好的地方找到这种性质的答案。语言规范中有一个link to the enhanced for loop。在那里你会发现它有几种不同类型的for语句,但它们都不会超过1次调用这个方法。


简单测试表明,该方法只调用一次

public class TestA { 
    public String [] theStrings; 

    public TestA() { 
     theStrings = new String[] {"one","two", "three"}; 
     for(String string : getTheStrings()) { 
      System.out.println(string); 
     } 
    } 

    public String[] getTheStrings() { 
     System.out.println("get the strings"); 
     return theStrings; 
    } 

    public static void main(String [] args) { 
     new TestA(); 
    } 
} 

输出:

get the strings 
one 
two 
three 

所以基本上他们是同样的事情。如果你想在for循环之外使用数组,唯一可能对2nd有利的是。


编辑

你让我好奇的处理上面我会这样使用的代码Java编译器如何反编译的类文件和继承人的结果是什么

public class TestA 
{ 

    public TestA() 
    { 
     String as[]; 
     int j = (as = getTheStrings()).length; 
     for(int i = 0; i < j; i++) 
     { 
      String string = as[i]; 
      System.out.println(string); 
     } 

    } 

    public String[] getTheStrings() 
    { 
     System.out.println("get the strings"); 
     return theStrings; 
    } 

    public static void main(String args[]) 
    { 
     new TestA(); 
    } 

    public String theStrings[] = { 
     "one", "two", "three" 
    }; 
} 

,你可以看到编译器简单地将你的for循环重构为一个标准循环!它也进一步证明,实际上它们在编译器完成之后完全相同。

+0

谢谢。现在我清楚地知道幕后究竟发生了什么。 – hamid

+3

*“简单的测试表明该方法只被调用一次”* - 简单的测试可能会误导......因为你不知道你应该测试什么。更好的方法是阅读JLS。 –

+0

+1对你的好解释 –

-1

第二个是更好的性能明智,它不是多次创建一个变量。因此,每次都会调用auth.getProjects。

+0

不正确。保存'auth.getProjects()'的局部变量被创建并分配一次。看我的答案。 –

+0

对不起,我在晚上写了xD – loafy

3

第二个例子中还有一些操作。不是直接使用方法引用数组,而是声明一个新的引用变量,将其存储在该新变量中,然后引用新变量。

您可以使用ASM Bytecode Outline查看字节码。

但是,这是微观优化。如果你需要一个新的参考变量,然后创建一个。如果你不需要,保存工作,不要创建一个新的工作。

+1

您的意思是“第二个例子”而不是“第一个例子”?第二个例子是声明一个新的引用变量的例子。尽管如果引用变量没有在函数的其他地方使用,大多数编译器都可能在这两种情况下使用寄存器。 –

+1

@WarrenDew是的,我编辑了我的答案,谢谢你指出, –

3

假设你的意思是in你的意思是:,性能没有区别;他们都做同样的事情。即使在第一个例子中,auth.getProjects()也只执行一次;它不能被执行多次,因为如果是的话,那么for迭代将不得不每次都开始,这不是它的工作原理。

我推荐使用你发现的版本更清晰。

-1

两者都与上面相同。

但是,做一件事情使用第一个程序,并在for循环之后,取消引用项目变量为 - projects = null; 否则,它会保持活着的完整方法生活,并会消耗不必要的记忆。

+0

不正确。 getProjects方法不被称为“在循环中”。它在循环的开始被调用一次。 –

+0

根据正确答案进行更改。 – 53by97

2

没有区别。

根据该JLS 14.14.2,增强for环路(用于阵列型)等效于常规for循环具有以下图案:

T[] #a = Expression; 
L1: L2: ... Lm: 
for (int #i = 0; #i < #a.length; #i++) { 
    {VariableModifier} TargetType Identifier = #a[#i]; 
    Statement 
} 

如果我们代替您的第一个例子:

for(String project : auth.getProjects()) { 
    // Do something with 'project' 
} 

我们得到:

String[] $a = auth.getProjects(); 
for (int $i = 0; $i < $a.length; $i++) { 
    String project = $a[$i]; 
    // Do something with 'project' 
} 

为您的第二个例子:

String[] projects = auth.getProjects(); 
for(String project : projects) { 
    // Do something with 'project' 
} 

我们得到:

String[] projects = auth.getProjects(); 
String[] $a = projects; 
for (int $i = 0; $i < $a.length; $i++) { 
    String project = $a[$i]; 
    // Do something with 'project' 
} 

代码的两个版本显然是等价的,并假设javac或JIT能够优化掉多余的局部变量$a的,这两个版本应该执行相同的操作。

请注意,在两种情况下,本地变量$a仅在循环开始时创建一次。