Для каждого итератора. Какой будет лучший вариант
Рассмотрим следующий сценарий.
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?
Ответы
Ответ 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.
Ответ 4
Лучший способ сделать это в java 8 is,
list.forEach(System.out::println);
Вот несколько полезных ссылок.
Ответ 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, но это может и не быть причиной.
![введите описание изображения здесь]()