这两个循环之间有什么区别吗?

时间:2014-05-04 04:14:47

标签: java performance loops

以下两个代码段之间在性能方面有什么不同吗?

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

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

对我而言,我认为第二个更好,但时间更长。第一个更短,但我不确定它是否更快。我不确定,但对我而言,似乎每次循环迭代时,都会调用auth.getProjects。那不是这样吗?

6 个答案:

答案 0 :(得分:10)

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


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

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循环之外使用数组。

<小时/> 的修改

你让我很好奇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循环重构为标准循环!它还进一步证明,在编译器完成它之后它们实际上是完全相同的。

答案 1 :(得分:3)

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

您可以使用ASM字节码大纲查看字节码。

但这是微优化。如果需要新的引用变量,请创建一个。如果您不需要,请保存工作,不要创建新工作。

答案 2 :(得分:3)

假设in你的意思是:,表现没有差别;他们都做同样的事情。即使在第一个例子中,auth.getProjects()只执行一次;它不能被执行多次,因为如果是的话,for迭代每次都要重新开始,这不是它的工作方式。

我建议使用你发现的版本更清楚。

答案 3 :(得分: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仅在循环开始时创建一次。

答案 4 :(得分:-1)

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

答案 5 :(得分:-1)

两者都与上面的相同。

但有一件事使用第一个approcah并且在for循环之后,取消引用项目变量as- projects = null; 否则,它将在整个方法生命中保持活力并消耗不必要的内存。