2014-05-04 3 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. Там вы обнаружите, что для выражений, которые он генерирует, существует несколько разных типов, но ни один из них не будет вызывать метод более одного раза.


Простой тест показывает, что метод вызывается только один раз

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 компилятор справилась с этой так, используя код, приведенный выше I декомпилированы файл класса и Херес, что результат

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

Оба те же, что указаны выше.

Но выполните одно использование 1-го утверждения и после цикла for, де-ссылку на переменную проектов as- 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 создается только в начале цикла в обоих случаях.

Смежные вопросы