Для оптимизации цикла

List<String> flowers = new ArrayList<String>();

Мой цикл for выглядит так:

for (int i = 0; i < flowers.size(); i++) {
...
}

ИЛИ я должен изменить это, чтобы выглядеть как приведенный ниже код

int size = flowers.size();
for (int i = 0; i < size; i++) {
...
}

Что более результативно (при условии, что у меня большой массив цветов), я предполагаю, что он должен быть последним.

4b9b3361

Лучше использовать для каждого цикла [более читаемый]

for (Flower flower :flowers){
    //...
}

Я сбросил инструкции, используя javap для следующего кода:

public void forLoop1() {
    List<String> lst = new ArrayList<String>();
    for (int i = 0; i < lst.size(); i++) {
        System.out.println("hi");
    }
}

public void forLoop2() {
    List<String> lst = new ArrayList<String>();
    int size = lst.size();
    for (int i = 0; i < size; i++) {
        System.out.println("hi");
    }
}

public void forLoop1();
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   iconst_0
   9:   istore_2
   10:  iload_2
   11:  aload_1
   12:  invokeinterface #4,  1; //InterfaceMethod java/util/List.size:()I
   17:  if_icmpge       34
   20:  getstatic       #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   23:  ldc     #6; //String hi
   25:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   28:  iinc    2, 1
   31:  goto    10
   34:  return

public void forLoop2();
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   invokeinterface #4,  1; //InterfaceMethod java/util/List.size:()I
   14:  istore_2
   15:  iconst_0
   16:  istore_3
   17:  iload_3
   18:  iload_2
   19:  if_icmpge       36
   22:  getstatic       #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   25:  ldc     #6; //String hi
   27:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   30:  iinc    3, 1
   33:  goto    17
   36:  return

Он не оптимизирован для меня.

java version "1.6.0_22" Java (TM) SE Среда выполнения (build 1.6.0_22-b04) Клиентская виртуальная машина Java HotSpot (TM) (сборка 17.1-b03, смешанный режим, обмен)

Итак, если вам нужно выбрать из упомянутых двух, зайдите на второй, но я лично поеду за for-each.


для каждого исполнения

Из пункта 46 в Эффективная Java от Джошуа Блоха:

Для каждого цикла, введенного в релиз 1.5, избавляется от беспорядка и возможность ошибки скрытие итератора или индексной переменной полностью. Полученная идиома в равной степени относится к коллекциям и массивы:

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}

Когда вы видите двоеточие (:), прочитайте его как "в." Таким образом, вышеприведенный цикл читается как "для каждого элемента e в элементах". Заметка что нет штрафа за исполнение для использования цикла for-each, даже для массивы. Фактически, это может дать небольшой преимущество над обычным для цикла в некоторых случаях, поскольку он вычисляет предел индекса массива только один раз. Хотя вы можете это сделать (пункт 45), программисты не всегда делайте это.


См. также

53
ответ дан 23 мая '11 в 9:24
источник

Извините, но ответ @Jigar неверен. Это правильный ответ. (TL;DR; не используйте for : each).

import java.util.ArrayList;
import java.util.List;

public class LoopTest {

    public static void main(String s[]) {

        long start, end;

        List<Integer> a =  new ArrayList<Integer>();

        for (int i = 0; i < 2500000; i++) {
            a.add(i);
        }

        ///// TESTING FOR : EACH LOOP

        start = System.currentTimeMillis();

        for (Integer j : a) {
            int x = j + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + " milli seconds for [ Integer j : a ] ");

        ////// TESTING DEFAULT LOOP

        start = System.currentTimeMillis();
        for (int i = 0; i < a.size(); i++) {
            int x = a.get(i) + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + " milli seconds for [ int i = 0; i < a.length; i++ ] ");


        ////// TESTING SLIGHTLY OPTIMIZED LOOP

        start = System.currentTimeMillis();
        int size = a.size();
        for (int i = 0; i < size; i++) {
            int x = a.get(i) + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + " milli seconds for [ int i = 0; i < size; i++ ] ");        

        //// TESTING MORE OPTIMIZED LOOP

        start = System.currentTimeMillis();
        for (int i = size; --i >= 0;) {
            int x = a.get(i) + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + " milli seconds for [ int i = size; --i >= 0; ] ");       

    }

}

Результаты:

96 milli seconds for [ Integer j : a ] 
57 milli seconds for [ int i = 0; i < a.length; i++ ] 
31 milli seconds for [ int i = 0; i < size; i++ ] 
31 milli seconds for [ int i = size; --i >= 0; ] 

Вы можете составить свой собственный разум, но слишком много атрибутов дается оптимизатору JVM. Вы все равно должны быть умны с помощью своего собственного кода, а использование обозначения for : each НЕ является хорошей идеей (почти всегда). Как вы можете видеть, у вас есть хорошая идея, поместив размер в свою переменную.

Несмотря на то, что некоторые из этих оптимизаций могут быть зависимыми от JVM (а некоторые могут работать с JIT), важно знать, что делает Java, и что Java не делает.

21
ответ дан 23 мая '11 в 9:46
источник

JVM не может оптимизировать его, поскольку size() - это метод, а JVM не может (и не пытается) определить, что size() всегда будет возвращать то же значение в этом контексте. При условии, что значение size() не изменяется, второе немного более показательно, но коэффициент усиления настолько, что он невелик, что вам даже не нужно даже его использовать.

11
ответ дан 23 мая '11 в 10:07
источник

Если производительность критическая, используйте простой контур счетчика, однако для 98% случаев ясность и простота кода гораздо важнее (например, 1000х или более), и вы должны использовать цикл for-each.

@Давид указывает, что использование счетчика происходит быстрее, но я бы отметил, что даже для 10 000 записей разница составляет субмикросекунду.

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

8
ответ дан 23 мая '11 в 10:29
источник

Поведение меняется, если список массивов изменяется во время итерации. Но я думаю, вы этого не делаете. Согласно моему тесту, последнее обычно быстрее (особенно в таких системах, как Android). Я бы написал это следующим образом:

for (int i = 0, size = flowers.size(); i < size; i++) {
    ...
}
5
ответ дан 23 мая '11 в 10:07
источник

Из спецификации языка Java (14.14.1):

Базовый для оператора выполняет некоторый код инициализации, затем выполняет Выражение, выражение и некоторый код обновления повторно, пока значение Выражение ложно.

Выражение i < flowers.size() в первом примере и оценивается один раз на каждой итерации. В вашем специальном случае он не должен иметь заметной разницы, потому что flowers.getSize() on ArrayList - очень короткий метод. Но, в общем случае, если результат выражения одинаковый для каждой итерации и дорогой, тогда сделайте предварительный расчет.

Следствие: это должно производить один и тот же вывод в каждой реализации виртуальной машины Java и доказывает, что выражение оценивается один раз на каждой итерации:

int counter2 = 10;
for (int counter1 = 0; counter1 < counter2; counter1++) {
  System.out.println(counter1 + ", " + counter2);
  counter2--;
}

Вывод:

0, 10
1, 9
2, 8
3, 7
4, 6
4
ответ дан 23 мая '11 в 9:46
источник

Лучшим вариантом является

[ int i = 0; i < size; i++ ]

Ваши результаты будут зависеть от того, какие JVM и другие настройки, такие как -client vs-server потому что некоторые из измерений настолько малы, что вам нужно использовать наносекунды для измерения, и вам нужно сделать много тестов, иначе вы в конечном итоге столкнетесь с GC с результатами. Также эти тесты имеют привычку JVM оптимизировать цикл до нуля. Я попытался устранить этот риск, поместив переменную, которую он изменяет в конце кода на экран.

1.6
-server
7.968242071 milli seconds for [ Integer j : a ] 
7.206275775999999 milli seconds for [ int i = 0; i < a.length; i++ ]  
1.5864E-5 milli seconds for [ int i = 0; i < size; i++ ] 
14.774186076999998 milli seconds for [ int i = size; --i >= 0; ] 

-client
83.36101683999999 milli seconds for [ Integer j : a ] 
44.288568631 milli seconds for [ int i = 0; i < a.length; i++ ]  
2.3191E-5 milli seconds for [ int i = 0; i < size; i++ ] 
24.826621246 milli seconds for [ int i = size; --i >= 0; ] 

1.7

-server
7.029150422 milli seconds for [ Integer j : a ] 
6.6269827779999995 milli seconds for [ int i = 0; i < a.length; i++ ]  
1.3852E-5 milli seconds for [ int i = 0; i < size; i++ ] 
13.842110377 milli seconds for [ int i = size; --i >= 0; ] 
13.868426141 milli seconds for [ int i = a.size()-1; i >= 0; i-- ] 
1.6618000000000003E-5 milli seconds for [ int i = 0; i < a.size(); i++ ] 

-client
7.382479727 milli seconds for [ Integer j : a ] 
6.748068759 milli seconds for [ int i = 0; i < a.length; i++ ]  
1.4162999999999998E-5 milli seconds for [ int i = 0; i < size; i++ ] 
13.951547335999999 milli seconds for [ int i = size; --i >= 0; ] 
13.929234053999998 milli seconds for [ int i = a.size()-1; i >= 0; i-- ] 
1.6873E-5 milli seconds for [ int i = 0; i < a.size(); i++ ] 

Тестовый код:

public static void main(String s[]) {
long start=0, end = 0, delta = 0;
//int[] a = new int[2500000];
List<Integer> a = new ArrayList<Integer>();
int x = 0;

for (int i = 0; i < 2500000; i++) {
    a.add(i);
}

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (Integer j : a) {
         x = j + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ Integer j : a ] ");


start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (int i = 0; i < a.size(); i++) {
         x = a.get(i) + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < a.length; i++ ]  ");

int size = a.size();

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.currentTimeMillis();

    for (int i = 0; i < size; i++) {
         x = a.get(i) + 3;
    }
    end = System.currentTimeMillis();
    delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < size; i++ ] ");

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (int i = size; --i >= 0;) {
         x = a.get(i) + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = size; --i >= 0; ] ");


start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (int i = a.size()-1; i >= 0; i--) {
         x = a.get(i) + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = a.size()-1; i >= 0; i-- ] ");

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.currentTimeMillis();

    for (int i = 0; i < a.size(); i++) {
         x = a.get(i) + 3;
    }
    end = System.currentTimeMillis();
    delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < a.size(); i++ ] ");        

System.out.println(x);
}
3
ответ дан 27 янв. '12 в 22:36
источник

Это просто пояснение на примере того, насколько ситуативно это.

Я тестировал выполнение "нормального" цикла for (int i = 0; i < list.size(); i++) и микро оптимизировал для цикла for (int i = -1, size = list.size(); ++i < size;). Я проверил тесты как в затмении из командной строки, так и заметил огромную разницу.

Результаты работы в eclipse:

Time for Original: 32552 ms   Time for MicroOptimized 32707 ms
Fastest Loop: Original
Slowest loop takes 0.47616121897272057% more time

Результаты запуска из командной строки:

Time for Original: 274489 ms   Time for MicroOptimized 30516 ms
Fastest Loop: MicroOptimized
Slowest loop takes 799.4920697339101% more time

Таким образом, в eclipse два цикла цикла принимают одно и то же время, но при запуске из командной строки исходная версия занимает на 800% больше времени, чем версия с микрооптимизацией. Величина разницы влечет мой разум. Я думаю, что eclipse использует другую JVM, которая применяет некоторые умные трюки оптимизации.

Это не означает, что вы должны начать использовать микро оптимизированную версию. Почти во всех случаях списки, которые вы перебираете, вероятно, будут настолько малы, что разница в производительности незначительна. И читаемость, полученная от использования стандартной версии, которую все узнают и понимают намного быстрее, более выгодна, чем незначительное увеличение производительности.

Для полноты, это код, который я выполнил:

public static void main(String[] args) {
        List<Byte> list = initializeList();
        byte value = 0;
        final int NUM_LOOPS = 100;

        long startOriginal, startOptimized, endOriginal, endOptimized;

        startOptimized = System.currentTimeMillis();
        for (int j = 0; j < NUM_LOOPS; j++) {
            for (int i = -1, size = list.size(); ++i < size;) {
                value = list.get(i);
            }
        }
        endOptimized = System.currentTimeMillis();

        startOriginal = System.currentTimeMillis();
        for (int j = 0; j < NUM_LOOPS; j++) {
            for (int i = 0; i < list.size(); i++) {
                value = list.get(i);
            }
        }
        endOriginal = System.currentTimeMillis();

        System.out.println(value);
        printResults(startOriginal, endOriginal, startOptimized, endOptimized);
    }

    private static void printResults(long startOriginal, long endOriginal,
            long startOptimized, long endOptimized) {

        long timeOriginal = endOriginal - startOriginal;
        long timeOptimized = endOptimized - startOptimized;

        long diff = Math.abs(timeOriginal - timeOptimized);
        long min = Math.min(timeOriginal, timeOptimized);

        System.out.println("Time for Original: " + timeOriginal + " ms"
                + "   Time for MicroOptimized " + timeOptimized + " ms");

        System.out.println("Fastest Loop: "
                + ((timeOriginal < timeOptimized) ? "Original"
                        : "MicroOptimized"));

        System.out.println("Slowest loop takes " + ((double) 100 * diff / min)
                + "% more time");       
    }

    public static List<Byte> initializeList(){
        List<Byte> list = new ArrayList<Byte>();
        final Byte ONE = new Byte((byte) 1);

        for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
            list.add(ONE);
        }

        return list;
    }
}
2
ответ дан 05 апр. '12 в 14:11
источник

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

import java.util.*;
public class TestForeach {
    public static void main (String[] args) {

        for (String s : getStrings()) {
            System.out.println("The string was: "+s);
        }
    } 

    private static List<String> getStrings() {
        System.out.println("IN GET STRINGS");
        return Arrays.asList("A","B","C");
    }
}

Это приведет к:

IN GET STRINGS
The string was: A
The string was: B
The string was: C

Следовательно, метод будет вызываться только один раз.

1
ответ дан 28 июня '17 в 15:03
источник

Чтобы избежать всей этой нумерации и итераторов и проверок при написании кода, используйте следующий простой наиболее читаемый код с максимальной производительностью. Почему это имеет максимальную производительность (детали подходят)

for (Object object : aCollection) { 
// Do something here
}

Если нужен индекс, то: Чтобы выбрать между двумя вышеуказанными формами: Второй - лучше, потому что вы используете локальную переменную для проверки. Когда метод выходит, переменная уходит в корзину файла.

0
ответ дан 11 мая '12 в 11:39
источник

Либо один будет делать. В зависимости от JVM второй может быть несколько тактов быстрее, но это будет неизмеримая или незначительная разница. Остерегайтесь этих типов суб-оптимизации. Если вы не создаете систему реального времени, где подсчитывается каждый тик процессора, они просто добавляют сложность и больше источников ошибок.

Я бы предложил использовать конструкцию итератора (как уже было предложено)

For (Flower flower: flowers) { ...

Это ясно, гибко и предикативно.

0
ответ дан 23 мая '11 в 9:59
источник

Мой подход немного отличается от этой проблемы. Для меня это не имеет значения, какой метод вы выберете. причина в том, что "улучшение производительности", которое вы получите в оптимальном оптимизированном методе, составит ~ 50 мс для 2500 000 итераций! (согласно сообщению @David). И, очевидно, это улучшение не похоже на то, что вы хотели бы потратить свое драгоценное время на поиск оптимизированного решения.

(Но все же, согласно оригинальному вопросу OP, я также хотел бы предложить последний подход.)

Я знаю, что ответ немного странный и не общий, но это реальность.

0
ответ дан 23 мая '11 в 10:42
источник

перейти к первому, только если оба они одинаковы, но создают еще одну избыточную переменную int во втором фрагменте кода.

Итак, перейдите к первому фрагменту кода

for (int i = 0; i < flowers.size(); i++) {
 ...
} 
-1
ответ дан 23 мая '11 в 9:27
источник
String d = JOptionPane.showInputDialog("enter start");
int s = Integer.parseInt(d);
String h = JOptionPane.showInputDialog("enter the end");
int z = Integer.parseInt(h);
for (int a = 1 ; a<10 ; a++) { 
    if (a%2 == 0 ) {
        JOptionPane.showMessageDialog(null, a);
        System.out.print(a);
    }
}    
-1
ответ дан 03 дек. '15 в 20:30
источник