LINUX.ORG.RU

for vs foreach

 , , ,


0

1

Что работает потенциально быстрее: for или forEach?

for (Object item : items) { ... }
// VS
items.forEach(item -> { ... });

Логика мне подсказывает, что между ними нет разницы (вообще). Интуитивно кажется, что если кто-то и будет быстрее, то for.

Попробовал запустить такой тест:

public class TestApplication {

  public static void main(final String[] args) {
    final List<String> items = new ArrayList<>();
    for (int i = 0; i < 10000000; i++) {
      items.add(String.format("Item #%d", i));
    }

    final AtomicInteger payload = new AtomicInteger();

    // test 1
    long testStart = System.currentTimeMillis();
    for (final String item : items) {
      payload.addAndGet(1);
    }
    long testStop = System.currentTimeMillis();
    System.out.println(String.format("Test 1 (for):     %d", testStop - testStart));

    // test 2
    testStart = System.currentTimeMillis();
    items.forEach(item -> {
      payload.addAndGet(1);
    });
    testStop = System.currentTimeMillis();
    System.out.println(String.format("Test 2 (forEach): %d", testStop - testStart));

    // test 3
    testStart = System.currentTimeMillis();
    for (final String item : items) {
      payload.addAndGet(1);
    }
    testStop = System.currentTimeMillis();
    System.out.println(String.format("Test 3 (for):     %d", testStop - testStart));

    // test 4
    testStart = System.currentTimeMillis();
    items.forEach(item -> {
      payload.addAndGet(1);
    });
    testStop = System.currentTimeMillis();
    System.out.println(String.format("Test 4 (forEach): %d", testStop - testStart));
  }

}

Результат:

Test 1 (for):     171
Test 2 (forEach): 162
Test 3 (for):     162
Test 4 (forEach): 145

(вывод по результатам нескольких запусков: forEach, как правило, работает быстрее for на ~ несколько миллисекунд)

Если убрать payload (запустить пустые циклы), то результат примерно такой (forEach опять быстрее):

Test 1 (for):     76
Test 2 (forEach): 73
Test 3 (for):     78
Test 4 (forEach): 70

Если заменить ArrayList на HashSet, то результаты примерно одинаковые (то for быстрее то forEach):

Test 1 (for):     431
Test 2 (forEach): 421
Test 3 (for):     368
Test 4 (forEach): 386
★★

Ответ на: комментарий от SaBo

forEach выглядит быстрее.

Что-то я сомневаюсь. Разве forEach не меняет как-то там контекст из-за которого, например, нельзя сделать следующую вещь:

int i = 0;
stringList.forEach(item -> {
   i++;
   ...
});

И приходится использовать костыли вроде: Поясните за Яву - зачем используют массив строк для одной строки?, TL;DR:

int[1] i = { 0 };
stringList.forEach(item -> {
   i[0]++;
   ...
});

Или это на уровне компилятора такие ограничения?

EXL ★★★★★
()

Все твои измерения не стоят ничего. Для тестов производительности есть такая штука https://mvnrepository.com/artifact/org.openjdk.jmh берешь ее пишешь бенмарки и убеждаешься, что быстрее на твоей машине.

Smetchik
()
Последнее исправление: Smetchik (всего исправлений: 1)

Что работает потенциально быстрее: for или forEach?

Всё упрётся в io.

А быстрее работают плюсы.

crutch_master ★★★★★
()
Ответ на: комментарий от SaBo

Да вам уже по делу все сказали, +- хрен редьки не слаще, для чистоты эксперимента позапускайте на разных ос, с разными вариантами jvm и убедитесь что картина будет немного иная, но в общем целом это все одно. Java достаточно умна чтобы выдавать приблизительно одинаковый байткод, что вы и сами прекрасно видите, если непонятен выхлоп почитайте спецификацию на jvm она более чем досконально описывает внутреннее устройство. Если и этого мало наши двери всегда открыты.

abcq ★★
()
Последнее исправление: abcq (всего исправлений: 1)

Не надоело? Сначала напиши, потом оптимизируй, как Кнут завещал

spec_po_kiskam ★★★
()
Ответ на: комментарий от xperious

Фигня в том, что ТС даже приблизительно не понимает, как работает то, что он тестирует, поэтому все его тесты - шлак.

Во-первых, for (как и foreach, лул) компилируется в совершенно разный код в зависимости от аргумента - для массива один, для интерфейса другой, для класса третий. ТС в своих тестах покрывает только один вариант.

Во-вторых, for полагается на итератор переданного объекта, соответственно, ТС замеряет производительность не цикла, а одной из библиотечных реализаций итератора.

В-третьих, foreach - это не конструкция языка, а метод, встроенный добрыми дядями в ArrayList. У разных коллекций он может работать совершенно по-разному. Кроме того, он использует всё тот же итератор (а ещё неявно созданный Consumer), так что по факту ТС замеряет скорость работы одной из библиотечных коллекций.

Ну и да, на разных JVM, ОС и платформах один и тот же байткод может совершенно по-разному JIT'иться и работать с разной скоростью.

Так что специально для ТС повторяю: либо

Читайте байткод jvm

, благо тулза для его просмотра есть в JDK из коробки, либо вон из профессии.

izzholtik ★★★
()
Последнее исправление: izzholtik (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.