Определить переменную внутри против снаружи в 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.)