Производительность традиционных для цикла по сравнению с Iterator/foreach в Java

Arrgh, я не могу заставить код обнаруживаться правильно. Извините, я получил его работа. Извините снова, я не думаю, что считал вопрос правильно.

String  foo[] = {"a","cc","a","dd"},
remove = "a";
boolean gaps[] = new boolean[foo.length];
int newlength = 0;

for (int c = 0; c<foo.length; c++)
{
    if (foo[c].equals(remove))
    {
        gaps[c] = true;
        newlength++;
    }
    else 
        gaps[c] = false;

    System.out.println(foo[c]);
}

String newString[] = new String[newlength];

System.out.println("");

for (int c1=0, c2=0; c1<foo.length; c1++)
{
    if (!gaps[c1])
    {
        newString[c2] = foo[c1];
        System.out.println(newString[c2]);
        c2++;
    }
}
59
задан aliteralmind 20 March 2014 в 18:47
поделиться

8 ответов

Предполагая, что это именно то, что вы имели в виду:

// traditional for loop
for (int i = 0; i < collection.size(); i++) {
  T obj = collection.get(i);
  // snip
}

// using iterator
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
  T obj = iter.next();
  // snip
}

// using iterator internally (confirm it yourself using javap -c)
for (T obj : collection) {
   // snip
}

Итератор работает быстрее для коллекций без произвольного доступа (например, TreeSet, HashMap, LinkedList). Для массивов и ArrayLists разница в производительности должна быть незначительной.

Edit: Я считаю, что микротестирование - это корень зла, как и ранняя оптимизация. Но опять же, я считаю, что хорошо иметь представление о последствиях таких довольно тривиальных вещей. Поэтому я выполнил небольшой тест :

  • перебираем LinkedList и ArrayList соответственно
  • со 100 000 «случайных» строк
  • , суммируя их длину (просто чтобы избежать оптимизации компилятором убрать весь цикл)
  • с использованием всех трех стилей цикла (итератор для каждого, для со счетчиком)

Результаты аналогичны для всех, кроме «для со счетчиком» с LinkedList. Всем остальным пяти потребовалось менее 20 миллисекунд, чтобы перебрать весь список. Использование list.get (i) в LinkedList 100 000 раз заняло более 2 минут (!) (В 60 000 раз медленнее). Вау! :) Следовательно, лучше всего использовать итератор (явно или неявно для каждого), особенно если вы не знаете, с каким типом и размером списка вы имеете дело.

80
ответ дан 24 November 2019 в 18:22
поделиться

Первая причина использовать итератор - это очевидная правильность . Если вы используете ручной указатель, могут быть очень безобидные отдельные ошибки, которые вы можете увидеть, только если внимательно присмотритесь: вы начали с 1 или с 0? Вы закончили на длине - 1 ? Вы использовали < или <= ? Если вы используете итератор, гораздо легче увидеть, что он действительно выполняет итерацию всего массива. «Говори, что делаешь, делай, что говоришь»

Вторая причина - это единый доступ к разным структурам данных. К массиву можно получить эффективный доступ через индекс, но связанный список лучше всего перемещаться, запоминая последний доступный элемент (иначе вы получите « Shlemiel the painter »). Хэш-карта еще сложнее. Предоставляя единообразный интерфейс для этих и других структур данных (например, вы также можете выполнять обход дерева), вы снова получаете очевидную корректность. Логика обхода должна быть реализована только один раз, и код, использующий ее, может кратко «сказать, что он делает, и сделать то, что он говорит»

22
ответ дан 24 November 2019 в 18:22
поделиться

В большинстве случаев производительность аналогична.

Однако, когда код получает список и зацикливается на нем, есть хорошо известный случай:
Итератор лучше подходит для все реализации List, которые не реализуют RandomAccess (пример: LinkedList).

Причина в том, что для этих списков доступ к элементу по индексу не является операцией с постоянным временем.

Так что вы также можете рассмотреть Iterator как более надежный (в отношении деталей реализации).


Как всегда, производительность не должна скрываться за проблемами читабельности.
Цикл java5 foreach - большой успех в этом аспекте: -)

4
ответ дан 24 November 2019 в 18:22
поделиться

I don't believe that

for (T obj : collection) {

calculates .size() each time thru the loop and is therefore faster than

for (int i = 0; i < collection.size(); i++) {
2
ответ дан 24 November 2019 в 18:22
поделиться

Одна из причин, по которой я научился придерживаться for each, заключается в том, что он упрощает вложенные циклы, особенно более 2-х мерных циклов. Все "i", "j" и "k", которыми вы можете в конечном итоге манипулировать, могут очень быстро запутаться.

1
ответ дан 24 November 2019 в 18:22
поделиться

Одна из лучших причин использовать итератор поверх синтаксиса i ++ заключается в том, что не все структуры данных будут поддерживать произвольный доступ, не говоря уже о том, чтобы он выполнялся хорошо. Вы также должны программировать интерфейс списка или коллекции, чтобы, если вы позже решили, что другая структура данных будет более эффективной, вы могли бы заменить ее без серьезной хирургической операции. В этом случае (в случае кодирования интерфейса) вам не обязательно знать детали реализации, и, вероятно, разумнее отложить это до самой структуры данных.

1
ответ дан 24 November 2019 в 18:22
поделиться

Используйте JAD или JD-GUI против вашего сгенерированного кода, и вы увидите, что реальной разницы нет. Преимущество новой формы итератора состоит в том, что она выглядит чище в вашей кодовой базе.

Edit : Из других ответов я вижу, что вы действительно имели в виду разницу между использованием get (i) и итератором. Я понял, что исходный вопрос означает разницу между старым и новым способами использования итератора.

1
ответ дан 24 November 2019 в 18:22
поделиться

+1 к тому, что сказал Сфуссенеггер. К вашему сведению, используете ли вы явный итератор или неявный (то есть для каждого), это не повлияет на производительность, потому что они компилируются с одним и тем же байтовым кодом.

0
ответ дан 24 November 2019 в 18:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: