Какая разница в производительности между следующими двумя циклами, если таковая имеется?
for (Object o: objectArrayList) {
o.DoSomething();
}
а также
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
java
performance
for-loop
Eflles
источник
источник
Ответы:
Из пункта 46 книги Джошуа Блоха « Эффективная Java »:
источник
Все эти петли делают то же самое, я просто хочу показать их, прежде чем бросить свои два цента.
Во-первых, классический способ перебора List:
for (int i=0; i < strings.size(); i++) { /* do something using strings.get(i) */ }
Во-вторых, предпочтительный способ, поскольку он менее подвержен ошибкам (сколько раз ВЫ делали «упс, смешали переменные i и j в этих циклах внутри циклов»?).
for (String s : strings) { /* do something using s */ }
В-третьих, микрооптимизированный цикл for:
int size = strings.size(); for (int i = -1; ++i < size;) { /* do something using strings.get(i) */ }
Теперь фактические два цента: по крайней мере, когда я их тестировал, третий был самым быстрым при подсчете миллисекунд того, сколько времени потребовалось для каждого типа цикла, когда простая операция в нем повторялась несколько миллионов раз - это было с использованием Java 5 с jre1.6u10 в Windows, если кому-то интересно.
Хотя, по крайней мере, кажется, что третий является самым быстрым, вы действительно должны спросить себя, хотите ли вы рискнуть реализовать эту оптимизацию глазка повсюду в вашем циклическом коде, поскольку, как я видел, на самом деле цикл не является t обычно самая трудоемкая часть любой реальной программы (или, может быть, я просто работаю не в том поле, кто знает). И также, как я уже упоминал в предлоге для цикла Java for-each (некоторые называют его циклом Iterator, а другими - циклом for-in ), вы с меньшей вероятностью столкнетесь с этой конкретной глупой ошибкой при ее использовании. И прежде чем обсуждать, как это может быть даже быстрее, чем другие, помните, что javac вообще не оптимизирует байт-код (ну, почти вообще), он просто компилирует его.
Если вы занимаетесь микрооптимизацией и / или ваше программное обеспечение использует множество рекурсивных циклов и тому подобное, то вас может заинтересовать третий тип цикла. Просто не забудьте хорошо протестировать свое программное обеспечение как до, так и после смены цикла for на этот странный, микрооптимизированный.
источник
get(int)
, другой - расширениеIterator
. Подумайте,LinkedList
где производительностьfor(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }
намного хуже, так как он работаетget(int)
n раз.Что ж, влияние на производительность в основном незначительное, но не нулевое. Если вы посмотрите на JavaDoc
RandomAccess
интерфейса:И для каждого цикла используется версия с итератором, поэтому,
ArrayList
например, цикл for-each не самый быстрый.источник
Iterable
s, но не массивы. Для массивов используется цикл for с индексной переменной. Информация об этом есть в JLS .Обычно предпочтительнее использовать цикл for-each. Подход «get» может быть медленнее, если используемая вами реализация List не поддерживает произвольный доступ. Например, если используется LinkedList, вы понесете затраты на обход, тогда как подход для каждого использует итератор, который отслеживает его позицию в списке. Подробнее о нюансах для каждого цикла .
Я думаю, что статья теперь здесь: новое место
Ссылка, показанная здесь, была мертва.
источник
К сожалению, разница есть.
Если вы посмотрите на сгенерированный байтовый код для обоих типов циклов, они будут разными.
Вот пример из исходного кода Log4j.
В /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java у нас есть статический внутренний класс Log4jMarker, который определяет:
/* * Called from add while synchronized. */ private static boolean contains(final Marker parent, final Marker... localParents) { //noinspection ForLoopReplaceableByForEach for (final Marker marker : localParents) { if (marker == parent) { return true; } } return false; }
Со стандартной петлей:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...); Code: 0: iconst_0 1: istore_2 2: aload_1 3: arraylength 4: istore_3 5: iload_2 6: iload_3 7: if_icmpge 29 10: aload_1 11: iload_2 12: aaload 13: astore 4 15: aload 4 17: aload_0 18: if_acmpne 23 21: iconst_1 22: ireturn 23: iinc 2, 1 26: goto 5 29: iconst_0 30: ireturn
Для каждого:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...); Code: 0: aload_1 1: astore_2 2: aload_2 3: arraylength 4: istore_3 5: iconst_0 6: istore 4 8: iload 4 10: iload_3 11: if_icmpge 34 14: aload_2 15: iload 4 17: aaload 18: astore 5 20: aload 5 22: aload_0 23: if_acmpne 28 26: iconst_1 27: ireturn 28: iinc 4, 1 31: goto 8 34: iconst_0 35: ireturn
Что случилось с ЭТОМ оракулом?
Я пробовал это с Java 7 и 8 в Windows 7.
источник
foreach делает намерение вашего кода более ясным, и это обычно предпочтительнее, чем очень незначительное улучшение скорости - если оно есть.
Всякий раз, когда я вижу проиндексированный цикл, мне приходится анализировать его немного дольше, чтобы убедиться, что он делает то, что я думаю. Например, он начинается с нуля, включает ли он конечную точку или исключает ее и т. Д.?
Похоже, что большую часть времени я трачу на чтение кода (который написал я или написал кто-то другой), и ясность почти всегда важнее производительности. В наши дни легко отказаться от производительности, потому что Hotspot делает такую потрясающую работу.
источник
Следующий код:
import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; interface Function<T> { long perform(T parameter, long x); } class MyArray<T> { T[] array; long x; public MyArray(int size, Class<T> type, long x) { array = (T[]) Array.newInstance(type, size); this.x = x; } public void forEach(Function<T> function) { for (T element : array) { x = function.perform(element, x); } } } class Compute { int factor; final long constant; public Compute(int factor, long constant) { this.factor = factor; this.constant = constant; } public long compute(long parameter, long x) { return x * factor + parameter + constant; } } public class Main { public static void main(String[] args) { List<Long> numbers = new ArrayList<Long>(50000000); for (int i = 0; i < 50000000; i++) { numbers.add(i * i + 5L); } long x = 234553523525L; long time = System.currentTimeMillis(); for (int i = 0; i < numbers.size(); i++) { x += x * 7 + numbers.get(i) + 3; } System.out.println(System.currentTimeMillis() - time); System.out.println(x); x = 0; time = System.currentTimeMillis(); for (long i : numbers) { x += x * 7 + i + 3; } System.out.println(System.currentTimeMillis() - time); System.out.println(x); x = 0; numbers = null; MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L); for (int i = 0; i < 50000000; i++) { myArray.array[i] = i * i + 3L; } time = System.currentTimeMillis(); myArray.forEach(new Function<Long>() { public long perform(Long parameter, long x) { return x * 8 + parameter + 5L; } }); System.out.println(System.currentTimeMillis() - time); System.out.println(myArray.x); myArray = null; myArray = new MyArray<Long>(50000000, Long.class, 234553523525L); for (int i = 0; i < 50000000; i++) { myArray.array[i] = i * i + 3L; } time = System.currentTimeMillis(); myArray.forEach(new Function<Long>() { public long perform(Long parameter, long x) { return new Compute(8, 5).compute(parameter, x); } }); System.out.println(System.currentTimeMillis() - time); System.out.println(myArray.x); } }
Дает следующий вывод в моей системе:
224 -699150247503735895 221 -699150247503735895 220 -699150247503735895 219 -699150247503735895
Я использую Ubuntu 12.10 alpha с OracleJDK 1.7 update 6.
В целом HotSpot оптимизирует множество косвенных обращений и простых избыточных операций, поэтому в целом вам не следует беспокоиться о них, если их не много в последовательности или они сильно вложены.
С другой стороны, индексированный get в LinkedList намного медленнее, чем вызов next on итератора для LinkedList, поэтому вы можете избежать этого снижения производительности, сохраняя читаемость при использовании итераторов (явно или неявно в цикле for-each).
источник
Единственный способ узнать наверняка - это протестировать его, и даже это не так просто, как может показаться . Компилятор JIT может делать с вашим кодом очень неожиданные вещи.
источник
Вот краткий анализ различий, проведенный командой разработчиков Android:
https://www.youtube.com/watch?v=MZOf3pOAM6A
Результатом является то , что есть разница, и в очень стесненных условиях с очень большими списками это может быть заметная разница. В их тестировании цикл for каждый занимал вдвое больше времени. Тем не менее, их тестирование длилось около 400 000 целых чисел. Фактическая разница на элемент в массиве составила 6 микросекунд . Я не тестировал, и они не сказали, но я ожидал, что разница будет немного больше с использованием объектов, а не примитивов, но даже все же, если вы не создаете код библиотеки, где вы не знаете масштаб того, что вас спросят чтобы перебрать, думаю, не стоит особо подчеркивать разницу.
источник
Даже с чем-то вроде ArrayList или Vector, где get - это простой поиск по массиву, второй цикл по-прежнему имеет дополнительные накладные расходы, которых нет в первом. Я ожидал, что он будет немного медленнее, чем первый.
источник
Всегда лучше использовать итератор вместо индексации. Это связано с тем, что итератор, скорее всего, оптимизирован для реализации List, в то время как индексированный (вызов get) может не быть. Например, LinkedList - это список, но индексация его элементов будет медленнее, чем итерация с использованием итератора.
источник
RandomAccess
списков. Обратите внимание, что это включает в себяArrayList
одну из наиболее часто используемых структур данных в Java. В большинстве случаев разница в производительности имеет меньшее значение, чем читаемость цикла, но штраф здесь, если он находится на вашем горячем пути, поэтому, когда задают вопрос о производительности, этот ответ действительно вводит в заблуждение.Судя по имени переменной
objectArrayList
, я предполагаю, что это экземплярjava.util.ArrayList
. В этом случае разница в производительности будет незаметна.С другой стороны, если это экземпляр
java.util.LinkedList
, второй подход будет намного медленнее, так какList#get(int)
это операция O (n).Таким образом, всегда предпочтительнее первый подход, если только индекс не нужен логике цикла.
источник
1. for(Object o: objectArrayList){ o.DoSomthing(); } and 2. for(int i=0; i<objectArrayList.size(); i++){ objectArrayList.get(i).DoSomthing(); }
Оба делают то же самое, но для простого и безопасного программирования для каждого из них существуют возможности для подверженности ошибкам при втором способе использования.
источник
Странно, что никто не упомянул очевидное - foreach выделяет память (в виде итератора), тогда как обычный цикл for не выделяет никакой памяти. Для игр на Android это проблема, потому что это означает, что сборщик мусора будет запускаться периодически. В игре вы не хотите, чтобы сборщик мусора работал ... НИКОГДА. Поэтому не используйте циклы foreach в методе рисования (или рендеринга).
источник
Принятый ответ отвечает на вопрос, за исключением исключительного случая ArrayList ...
Поскольку большинство разработчиков полагаются на ArrayList (по крайней мере, я так считаю)
Поэтому я обязан добавить сюда правильный ответ.
Прямо из документации разработчика: -
Расширенный цикл for (также известный как цикл «for-each») можно использовать для коллекций, реализующих интерфейс Iterable, и для массивов. В коллекциях итератор выделяется для выполнения интерфейсных вызовов hasNext () и next (). С ArrayList рукописный счетный цикл примерно в 3 раза быстрее (с JIT или без него), но для других коллекций улучшенный синтаксис цикла for будет точно эквивалентен явному использованию итератора.
Есть несколько альтернатив для перебора массива:
static class Foo { int mSplat; } Foo[] mArray = ... public void zero() { int sum = 0; for (int i = 0; i < mArray.length; ++i) { sum += mArray[i].mSplat; } } public void one() { int sum = 0; Foo[] localArray = mArray; int len = localArray.length; for (int i = 0; i < len; ++i) { sum += localArray[i].mSplat; } } public void two() { int sum = 0; for (Foo a : mArray) { sum += a.mSplat; } }
zero () является самым медленным, потому что JIT еще не может оптимизировать затраты на получение длины массива один раз для каждой итерации цикла.
one () быстрее. Он помещает все в локальные переменные, избегая поиска. Только длина массива дает преимущество в производительности.
two () является самым быстрым для устройств без JIT и неотличим от one () для устройств с JIT. Он использует расширенный синтаксис цикла for, представленный в версии 1.5 языка программирования Java.
Таким образом, вы должны использовать расширенный цикл for по умолчанию, но подумайте о написанном от руки цикле с подсчетом для критичной для производительности итерации ArrayList.
источник
public class FirstJavaProgram { public static void main(String[] args) { int a[]={1,2,3,45,6,6}; // Method 1: this is simple way to print array for(int i=0;i<a.length;i++) { System.out.print(a[i]+" "); } // Method 2: Enhanced For loop for(int i:a) { System.out.print(i+" "); } } }
источник