Подтвердить что ты не робот

Для каждого итератора. Какой будет лучший вариант

Рассмотрим следующий сценарий.

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

Теперь я добавил значения String для этого списка.

Я использовал следующие способы перехода к каждому элементу в списке.

Вариант один - используйте for-each

for (String i : list) {
        System.out.println(i);
    } 

Вариант два - использование Iterator

Iterator it=list.iterator();
while (it.hasNext()){
   System.out.println(it.next());
}

Я просто хочу знать, есть ли преимущество в производительности, если я использую for-each вместо Iterator. А также неуместная практика использования Iterator теперь дней в Java?

4b9b3361

Ответ 1

for-each является синтаксическим сахаром для использования iterators (подход 2).

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

for (String i : list) {
    System.out.println(i);
    list.remove(i); // throws exception
} 

Iterator it=list.iterator();
while (it.hasNext()){
    System.out.println(it.next());
    it.remove(); // valid here
}

Ответ 2

Разница в основном является синтаксическим сахаром, за исключением того, что итератор может удалять элементы из коллекции, которые он выполняет итерацию. Технически, расширенные для циклов позволяют вам перебирать все, что Iterable, которое, как минимум, включает как сборники, так и массивы.

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

for (String s : stringList) { ... }

против

for (Iterator<String> iter = stringList.iterator(); iter.hasNext(); ) {
  String s = iter.next();
  ...
}

Ответ 3

for-each - это усовершенствованная конструкция цикла. Внутри он создает Итератор и выполняет итерацию над Коллекцией. Только возможное преимущество использования фактического объекта Iterator над конструкцией for-each заключается в том, что вы можете изменить свою коллекцию, используя методы Iterator, такие как .remove(). Изменение коллекции без использования методов Iterator во время итерации приведет к ConcurrentModificationException.

Ответ 5

Простые ответы: Нет и нет.

Внутренне цикл for-each создает Iterator для итерации по коллекции.

Преимущество использования Iterator явно заключается в том, что вы можете получить доступ к методу Iterator.

Ответ 6

Если вы хотите заменить элементы в своем списке, я бы пошел в старую школу с циклом for

for (int nIndex=0; nIndex < list.size(); nIndex++) {
  Obj obj = (Obj) list.get(nIndex);

  // update list item
  list.set(nIndex, obj2);
}

Ответ 7

foreach все равно использует итераторы под капотом. Это действительно просто синтаксический сахар.

Рассмотрим следующую программу:

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

public class Whatever {
    private final List<Integer> list = new ArrayList<>();
    public void main() {
        for(Integer i : list) {
        }
    }
}

Скомпилируйте его с помощью javac Whatever.java,
И прочитайте дизассемблированный байт-код main(), используя javap -c Whatever:

public void main();
  Code:
     0: aload_0
     1: getfield      #4                  // Field list:Ljava/util/List;
     4: invokeinterface #5,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
     9: astore_1
    10: aload_1
    11: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
    16: ifeq          32
    19: aload_1
    20: invokeinterface #7,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
    25: checkcast     #8                  // class java/lang/Integer
    28: astore_2
    29: goto          10
    32: return

Мы видим, что foreach скомпилируется до программы, которая:

  • Создает итератор с помощью List.iterator()
  • Если Iterator.hasNext(): вызывает Iterator.next() и продолжает цикл

Что касается "почему этот бесполезный цикл не оптимизирован из скомпилированного кода, мы можем видеть, что он ничего не делает с элементом списка": ну, вы можете запрограммировать свой итерабельный, чтобы .iterator() имеет побочные эффекты или что .hasNext() имеет побочные эффекты или значимые последствия.

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

Итак, несмотря на то, что мы можем доказать, что в теле цикла ничего не происходит... более дорого (неразрешимо?) доказывать, что ничего значимого/косвенного не происходит, когда мы итерируем. Компилятор должен оставить это пустое тело цикла в программе.

Лучшее, на что мы могли надеяться, было бы предупреждением компилятора. Интересно, что javac -Xlint:all Whatever.java не предупреждает нас об этом пустом теле цикла. IntelliJ IDEA делает. По общему признанию, я настроил IntelliJ на использование компилятора Eclipse, но это может и не быть причиной.

введите описание изображения здесь