Какая разница в производительности между следующими двумя циклами, если таковая имеется?
for (Object o: objectArrayList) {
o.DoSomething();
}
и
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
в java он называется «для каждого», но когда дело доходит до Objective C, он называется циклом «for In».
См. Также: docs.oracle.com/javase/8/docs/api/java/util/RandomAccess.htm l
Расширенная производительность цикла обсуждается здесь: stackoverflow.com/questions/12155987/…




Из пункта 46 в Эффективная Java Джошуа Блоха:
The for-each loop, introduced in release 1.5, gets rid of the clutter and the opportunity for error by hiding the iterator or index variable completely. The resulting idiom applies equally to collections and arrays:
// The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); }When you see the colon (:), read it as “in.” Thus, the loop above reads as “for each element e in elements.” Note that there is no performance penalty for using the for-each loop, even for arrays. In fact, it may offer a slight performance advantage over an ordinary for loop in some circumstances, as it computes the limit of the array index only once. While you can do this by hand (Item 45), programmers don’t always do so.
Стоит упомянуть, что в цикле for-each нет возможности получить доступ к счетчику индекса (поскольку он не существует)
Да, но этот счетчик теперь виден за пределами цикла. Конечно, это простое решение, но оно подходит для всех!
Распределение итератора снижает производительность. У меня был очень параллельный код в живых обоях Android. Я видел, что сборщик мусора сходил с ума. Это произошло потому, что для каждого цикла были выделены временные итераторы во многих различных (недолговечных) потоках, что заставляло сборщик мусора выполнять большую работу. Переключение на обычные циклы на основе индексов устранило проблему.
@ gsingh2011 Но это также зависит от того, используете ли вы список с произвольным доступом или нет. Полагаю, использование доступа на основе индекса к спискам с произвольным доступом будет намного хуже, чем использование for-each со списками произвольного доступа. Если вы работаете с интерфейсом List и, следовательно, не знаете фактический тип реализации, вы можете проверить, является ли список экземпляром (реализует) RandomAccess, если вам это действительно так важно: docs.oracle.com/javase/8/docs/api/java/util/RandomAccess.htm l
@ gsingh2011 В документации по Android (developer.android.com/training/articles/perf-tips.html#Loop s) упоминается, что у вас будет снижение производительности при использовании foreach только для ArrayList, а не для других коллекций. Мне любопытно, было ли это твоим случаем.
@Viccari Нет, это другое. Это чисто стоимость выделения / освобождения, которая требуется при использовании цикла foreach для создания объекта Iterator, она не связана с самой итерацией. В большинстве случаев это то, о чем вам не нужно беспокоиться. Если он находится на горячем пути вашего кода, это может вызвать проблемы с производительностью, как это было со мной.
@basszero у вас всегда доступа к индексу с indexOf() нет?
@Viccari +1 за упоминание случая с ArrayList. Я также читал, что для ArrayList традиционный цикл for в 3 раза быстрее, чем для каждого цикла.
Как указал Виккари, это неверно для ArrayList, и это DS, который большинство разработчиков используют изо дня в день.
Трудно поверить, что Блох дает такой вводящий в заблуждение совет; Я надеюсь, что это было исправлено или будет исправлено в будущих версиях. В частности, утверждение «нет потери производительности при использовании цикла for-each» просто неверно, как указали @Viccari и другие.
Даже с чем-то вроде ArrayList или Vector, где get - это простой поиск по массиву, второй цикл по-прежнему имеет дополнительные накладные расходы, которых нет в первом. Я ожидал, что он будет немного медленнее, чем первый.
Первый цикл тоже должен получить каждый элемент. Для этого он незаметно создает итератор. Они действительно эквивалентны.
Думая в терминах C, итератор может просто увеличивать указатель, но get должен каждый раз умножать значение i на ширину указателя.
Это зависит от того, какой тип списка вы используете. Я думаю, что вы правы, использование get никогда не будет быстрее, а иногда и медленнее.
Единственный способ узнать наверняка - это протестировать его, и даже это не так просто, как может показаться. Компилятор JIT может делать с вашим кодом очень неожиданные вещи.
Обычно предпочтительнее использовать цикл for-each. Подход «get» может быть медленнее, если используемая вами реализация List не поддерживает произвольный доступ. Например, если используется LinkedList, вы понесете затраты на обход, тогда как подход для каждого использует итератор, который отслеживает его позицию в списке. Подробнее о нюансы для каждого цикла.
Думаю, статья сейчас здесь: Новое место
Ссылка, показанная здесь, была мертва.
Все эти петли делают то же самое, я просто хочу показать их, прежде чем бросить свои два цента.
Во-первых, классический способ перебора 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-in), вы с меньшей вероятностью столкнетесь с этой одной конкретной глупой ошибкой при ее использовании. И прежде чем обсуждать, как это может быть даже быстрее, чем другие, помните, что javac вообще не оптимизирует байт-код (ну, почти вообще), он просто компилирует его.
Если вы занимаетесь микрооптимизацией и / или ваше программное обеспечение использует множество рекурсивных циклов и тому подобное, то вас может заинтересовать третий тип цикла. Просто не забудьте хорошо протестировать свое программное обеспечение как до, так и после замены имеющихся циклов for на этот нечетный, микрооптимизированный.
Обратите внимание, что цикл for с ++ i <= size основан на «1», например метод get внутри цикла будет вызываться для значений 1, 2, 3 и т. д.
Лучший способ написать микрооптимизированный цикл - for (int i = 0, size = strings.size (); ++ i <= size;) {} Это предпочтительнее, поскольку минимизирует объем
Разве третий не начинается с i = 1 при первом прохождении цикла, пропуская первый элемент. и в этом цикле for нет необходимости. int n = длина строки; while (n -> 0) {System.out.println ("" + n + "" + строки [n]); }
@ Dónal этот шаблон цикла пропускает первый и дает IOOBE. Это работает: for (int i = -1, size = list.size (); ++ i <size;)
«Все эти петли делают одно и то же» неверно. Один использует get(int) с произвольным доступом, другой - Iterator. Рассмотрим LinkedList, где производительность for(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ } намного хуже, поскольку он выполняет get(int) n раз.
Всегда лучше использовать итератор вместо индексации. Это связано с тем, что итератор, скорее всего, оптимизирован для реализации List, в то время как индексированный (вызов get) может не быть. Например, LinkedList - это список, но индексация его элементов будет медленнее, чем итерация с использованием итератора.
Я думаю, что в оптимизации производительности не бывает такого понятия, как «всегда».)
Я думаю, вам нужно определить здесь лучше. Если мы говорим о производительности (о чем идет речь), то здесь просто неправильно говорить всегда. Циклы на основе итераторов медленнее, чем индексированные циклы для списков RandomAccess. Обратите внимание, что сюда входит ArrayList, одна из наиболее часто используемых структур данных в Java. В большинстве случаев разница в производительности имеет меньшее значение, чем удобочитаемость цикла, но штраф здесь, если он находится на вашем горячем пути, поэтому, когда задают вопрос о производительности, этот ответ действительно вводит в заблуждение.
foreach делает намерение вашего кода более ясным, и это обычно предпочтительнее, чем очень незначительное улучшение скорости - если оно есть.
Всякий раз, когда я вижу проиндексированный цикл, мне приходится анализировать его немного дольше, чтобы убедиться, что он делает то, что я считать, например. Начинается ли он с нуля, включает ли он конечную точку или исключает ее и т. д.?
Похоже, что большую часть времени я трачу на чтение кода (который написал я или написал кто-то другой), и ясность почти всегда важнее производительности. В наши дни легко отказаться от производительности, потому что Hotspot делает такую потрясающую работу.
Что ж, влияние на производительность в основном незначительное, но не нулевое. Если вы посмотрите на JavaDoc интерфейса RandomAccess:
As a rule of thumb, a List implementation should implement this interface if, for typical instances of the class, this loop:
for (int i=0, n=list.size(); i < n; i++) list.get(i);runs faster than this loop:
for (Iterator i=list.iterator(); i.hasNext();) i.next();
И цикл for-each использует версию с итератором, поэтому для ArrayList, например, цикл for-each не самый быстрый.
Неужели каждый? Даже с массивом? Я прочитал здесь stackoverflow.com/questions/1006395/…, что здесь нет итератора.
@ OndraŽižka: цикл for-each использует итераторы при цикле по Iterable, но не массивы. Для массивов используется цикл for с индексной переменной. Информация об этом есть в JLS.
да, поэтому вам сначала нужно будет создать его в массиве с toArray, имеющим стоимость.
Следующий код:
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).
По имени переменной objectArrayList я предполагаю, что это экземпляр java.util.ArrayList. В этом случае разница в производительности будет незаметна.
С другой стороны, если это экземпляр java.util.LinkedList, второй подход будет намного медленнее, поскольку List#get(int) является операцией O (n).
Таким образом, всегда предпочтительнее первый подход, если только индекс не нужен логике цикла.
К сожалению, разница есть.
Если вы посмотрите на сгенерированный байтовый код для обоих типов циклов, они будут разными.
Вот пример из исходного кода 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.
Для тех, кто пытается прочитать дизассемблированный код, в конечном итоге код, сгенерированный внутри цикла, идентичен, но для каждой настройки создается дополнительная временная переменная, содержащая ссылку на второй аргумент. Если дополнительная скрытая переменная зарегистрирована, но сам параметр не во время генерации кода, то для каждого будет быстрее; если параметр зарегистрирован в примере for (;;), время выполнения будет идентичным. Нужен тест?
1. for(Object o: objectArrayList){
o.DoSomthing();
}
and
2. for(int i=0; i<objectArrayList.size(); i++){
objectArrayList.get(i).DoSomthing();
}
Оба делают то же самое, но для простого и безопасного программирования для каждого из них существуют возможности для подверженности ошибкам при втором способе использования.
Вот краткий анализ различий, проведенный командой разработчиков Android:
https://thewikihow.com/video_MZOf3pOAM6A
В результате есть разница является, и в очень ограниченных средах с очень большими списками это может быть заметной разницей. В их тестировании цикл for каждый занимал вдвое больше времени. Тем не менее, их тестирование длилось более 400 000 целых чисел. Фактическая разница на элемент в массиве составила 6 микросекунды. Я не тестировал, и они не сказали, но я ожидал, что разница будет немного больше при использовании объектов, а не примитивов, но даже все же, если вы не создаете код библиотеки, где вы не знаете масштаб того, что вас спросят чтобы повторить, думаю, не стоит особо подчеркивать разницу.
Странно, что никто не упомянул очевидное - 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+" ");
}
}
}
@Keparo: это цикл "для каждого", а не цикл "for-in"