Определить переменную внутри против снаружи в Java?

Предположим, у нас есть двойной цикл с большим количеством итераций. Должны ли мы определить переменную вне цикла для ускорения? Просто для примера:

for(int i=0;i<2000;i++)
     for(int j=0;j<1000;j++)
          System.out.println(i+j);

Поскольку мы инициализируем j каждый раз, когда я изменяю значение, лучше объявить j снаружи, например:

 for(int i=0;i<2000;i++){
     int j=0;
     for(j=0;j<1000;j++)
          System.out.println(i+j); 
  }

Для себя мне нравится первый способ, он более читабелен. Однако я не уверен, что второй способ ускорит программу или нет?

3 ответа

Код, сгенерированный Javac, точно такой же, поэтому не может быть никакой разницы:

Имеется файл "Test.java":

public class Test {

    public static int a(int[][] v) {
        int sum = 0;
        int rows = v.length;
        int cols = v[0].length; // yes, this fails if v[0] is null
        for (int j=0; j<rows; j++) {
            for (int i=0; i<cols; i++) {
                sum += v[j][i];
            }           
        }
        return sum;
    }

    public static int b(int[][] v) {
        int sum = 0;
        int rows = v.length;
        int cols = v[0].length; // yes, this fails if v[0] is null
        int j, i;
        for (j=0; j<rows; j++) {
            for (i=0; i<cols; i++) {
                sum += v[j][i];
            }           
        }
        return sum;
    }
}

Компилировать (javac Test.java) и посмотрите на байт-код (результат javap -c Test) в файле классов. Вот b:

public static int b(int[][]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   aload_0
   3:   arraylength
   4:   istore_2
   5:   aload_0
   6:   iconst_0
   7:   aaload
   8:   arraylength
   9:   istore_3
   10:  iconst_0
   11:  istore  4
   13:  iload   4
   15:  iload_2
   16:  if_icmpge   50
   19:  iconst_0
   20:  istore  5
   22:  iload   5
   24:  iload_3
   25:  if_icmpge   44
   28:  iload_1
   29:  aload_0
   30:  iload   4
   32:  aaload
   33:  iload   5
   35:  iaload
   36:  iadd
   37:  istore_1
   38:  iinc    5, 1
   41:  goto    22
   44:  iinc    4, 1
   47:  goto    13
   50:  iload_1
   51:  ireturn

Вы получаете точно такой же байт-код для обоих a а также b

Нет, вторая форма ничем не лучше (она даже не скомпилируется, потому что вы пропустили раздел инициализации цикла for)!

Оператор for построен следующим образом: for (init, expression, update) statement

  • init оператор будет выполняться только ОДИН РАЗ перед началом цикла

  • expression Оператор будет выполняться перед каждой итерацией цикла. Только если это приводит к trueцикл будет продолжен.

  • update Оператор будет выполняться сразу после каждой итерации.

  • statement это утверждение, которое будет выполняться на каждой итерации.

Так что это просто краткая форма:

init;
while (expression) {
    statement;
    update;
}

Как init будет выполняться только один раз, это не оптимизация, если вы поставите его перед for петля.

В этом нет никакого смысла. Встроенные типы не имеют значительной стоимости установки.

С предметами дело обстоит иначе.

Вы должны предварительно объявить Объекты вне цикла по соображениям производительности. Я проверил это с JDK 1.4, с тех пор больше никогда. Возможно, более новые JDK оптимизируют это.

for(int i=0;i<2000;i++)
   for(int j=0;j<1000;j++)
      String x = "Hello " + j;

заметно медленнее, чем (определите внешний цикл)

String x;
for(int i=0;i<2000;i++)
   for(int j=0;j<1000;j++)
      x = "Hello " + j;

заметно медленнее, чем (последний в цикле)

for(int i=0;i<2000;i++)
   for(int j=0;j<1000;j++)
       final String x = "Hello " + j;

обновление: только что запущено время с JDK 7u45, 1000 x 1000 итераций

Я установил код для запуска циклов более одного раза и для сохранения переменных в массиве результатов. Другой прогон не будет хранить переменные вне цикла.

  • Распределение строк в цикле и сохранение в массиве: Avg 109,49 / NoGC: 47-49 (*)
  • Распределение строк в цикле и без хранения: Avg 43,5 / NoGC: 39-47

  • Выделение строки вне цикла и сохранение в массиве: Avg 98,9 / NoGC: 42-44

  • Распределение строк вне цикла и без сохранения: Avg 28.25 / NoGC: 27-32

  • окончательное выделение строки в цикле и сохранение в массиве: Avg 98,92 / NoGC: 42-50

  • окончательное распределение строк в цикле и без сохранения: Avg 28.31 / NoGC: 27-34

Результаты показывают, что объявление объекта берет свое. Но настоящий драйвер - это GC. Эффект объявления внутри цикла или за его пределами невелик со временем, если значение используется вне цикла. Использование последней строки внутри цикла не будет иметь никакого значения. Если вы не сохраняете за пределами цикла, разница между объявлением переменной внутри цикла или за ее пределами заметна, final, опять же, не имеет значения.

(* При сохранении всех значений большая часть времени была потеряна в прогонах GC, поэтому я добавил диапазон прогонов без GC.)

Другие вопросы по тегам