2013-08-29 5 views
31

Рассмотрим следующую ситуацию.Для каждого итератора. Какой будет лучший вариант

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?

+4

Я думаю, что для каждого использует итератор –

+0

возможный дубликат http://stackoverflow.com/questions/2113216/which-is-more-efficient-a-for-each-loop-or-an-iterator –

ответ

68

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 
} 
+7

Of Конечно, хорошо помнить, что добавление/удаление элементов из коллекции, которую вы зацикливаете, не является хорошей практикой, по той причине, что вы выделили. – Jeremy

1

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

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

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

9

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

+2

* Can * not "will" привел к ConcurrentModificationException –

+0

Одна путаница здесь, если для каждого из них используется Iterator внутри, почему существует ограничение при удалении элементов во время итерации? – shaun

+0

@shaun, потому что у вас нет доступа к нему :) –

14

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

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

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

против:

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

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

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

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

Лучший способ сделать это в Java 8,

list.forEach(System.out::println); 

Вот несколько полезных ссылок.

  1. Java 8 Iterable.forEach() vs foreach loop

  2. http://www.javaworld.com/article/2461744/java-language/java-language-iterating-over-collections-in-java-8.html

  3. https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html

+0

почему вы думаете, что лучше использовать list.foreach()? в качестве такого метода feaeach() вы не можете модифицировать extern var и копировать его как final или обернуть его в массив. –

0

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() (например, связаться с базой данных или закрыть курсор, потому что вы достигли конца набора результатов).

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

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

enter image description here

0

Вот простой фрагмент кода, чтобы проверить работу For-each против Iterator против for для обхода ArrayList<String>, выполненного на Java версии 8.

 long MAX = 2000000; 

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

     for (long i = 0; i < MAX; i++) { 

      list.add("" + i); 
     } 

     /** 
     * Checking with for each iteration. 
     */ 
     long A = System.currentTimeMillis(); 

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

     long B = System.currentTimeMillis(); 
     System.out.println(B - A + "ms"); 

     /** 
     * Checking with Iterator method 
     */ 

     Iterator<String> iterator = list.iterator(); 
     while (iterator.hasNext()) { 
      iterator.next(); 
      // System.out.println(iterator.next()); 
     } 

     long C = System.currentTimeMillis(); 
     System.out.println(C - B + "ms"); 

     /** 
     * Checking with normal iteration. 
     */ 
     for (int i = 0; i < MAX; i++) { 
      list.get((int) (i % (MAX - i))); 
      // System.out.println(list.get(i)); 
     } 

     long D = System.currentTimeMillis(); 
     System.out.println(D - C + "ms"); 

Средние выходные значения:

19ms 
9ms 
27ms 

Результат анализа: Iterator (9ms) < For-each (19ms) < For (27ms)

Здесь Iterator имеет лучшую производительность и For имеет наименьшую производительность. Однако производительность For-each находится где-то посередине.