Расширенные для работы в петле

У меня был спор с моим другом относительно этого. Рассмотрим приведенный ниже фрагмент,

for(i=0; i<someList.size(); i++) {
    //some logic
    }

Здесь someList.size() будет выполняться для каждой итерации, поэтому рекомендуется выполнить перенос этого размера на внешний (до) цикл.

Теперь, когда я использую расширенный цикл для этого типа,

for(SpecialBean bean: someBean.getSpecialList()) {
//some logic
}

Нужно ли перемещать someBean.getSpecialList() за пределы цикла? Сколько раз будет выполняться someBean.getSpecialList(), если я должен сохранить второй фрагмент, как есть?

Ответ 1

Более серьезная проблема с вашим первым примером заключается в том, что она приведет к производительности O (n 2), если применяется к LinkedList. В зависимости от обстоятельств вычисление размера списка может принимать O (n), поэтому еще один источник сложности O (n 2), но размер с большей вероятностью будет кэшироваться.

Второй пример скомпилируется с помощью Iterator и будет оценивать someBean.getSpecialList().iterator() только один раз.

Ответ 2

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

Цикл for-each, введенный в релиз 1.5, избавляется от беспорядка и возможность ошибки, скрывая итератор или индекс переменная полностью. Результирующая идиома применяется в равной степени к коллекции и массивы:

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

См. также is-there-a-performance-difference-between-a-for-loop-and-a-for-each-loop

Ответ 3

for each вариант будет таким же, как ниже

for (Iterator i = c.iterator(); i.hasNext(); ) {
doSomething((Element) i.next()); 
}

От Пункт 46: Предпочитает для каждой петли традиционную для циклов Эффективной java

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

Итак, моя первая догадка была неправильной: нет функции штрафа с помощью функции внутри цикла for each.

Ответ 4

Альтернативой первому фрагменту будет:

for(i=0, l=someList.size(); i<l; i++) {
    //some logic
}

Что касается цикла for..each, вызов getSpecialList() будет выполнен только один раз (вы можете проверить это, добавив некоторую отладку/протоколирование внутри метода).

Ответ 5

Поскольку расширенный цикл использует Iterator, взятый из Iterable, было бы невозможно или разумно выполнить someBean.getSpecialList() более одного раза. Перемещение его за пределы цикла не изменит производительность цикла, но вы можете сделать это, если это улучшит читаемость.

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