Java: лучший способ перебрать коллекцию (здесь ArrayList)

98

Сегодня я с удовольствием писал код, когда добрался до фрагмента кода, который уже использовал сотни раз:

Итерации по коллекции (здесь ArrayList)

По какой-то причине я действительно посмотрел на параметры автозаполнения Eclipse, и это заставило меня задуматься:

В каких случаях следующие циклы лучше использовать, чем другие?

Классический цикл индексации массива:

for (int i = 0; i < collection.length; i++) {
  type array_element = collection.get(index);
}

Итератор hasNext () / next ():

for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
  type type = (type) iterator.next();   
}

И мой любимый, потому что его так просто написать:

for (iterable_type iterable_element : collection) {

}
Джейсон Роджерс
источник
2
Что касается меня, я в основном использую 3-й цикл.
Гарри Джой
1
Для второго способа лучше использовать:for (Iterator<type> iterator = collection.iterator(); iterator.hasNext();) { type type = iterator.next(); }
Майк Джонс
4
Интерфейс коллекции не содержит метода get, поэтому первый не всегда возможен.
ThePerson

Ответы:

104

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

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

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

MAK
источник
3
+1 Обыграй меня. собирался упомянуть LinkedList (не все из Collection них дешево получить по индексу).
The Scrum Meister
Пока требуется просто зацикливать элементы, всегда хорошо использовать 3-ю версию, потому что реализация уже существует для всех различных коллекций, а Sun или любая реализация JDK стремится улучшить производительность, а не каждую.
Phani 08
@Phani: два других варианта также работают с любой реализацией JDK.
MAK
Я не говорил, что это не сработает, но если по какой-либо причине для реализаций коллекций будут сделаны определенные улучшения или изменения производительности, тогда это будет автоматически применяться для вашего кода, и вам не нужно писать для повышения производительности вот что я имею в виду.
Phani 08
1
@Phani: AFAIK 3-я форма - это просто синтаксический сахар для 2-й формы (т.е. под капотом вариант foreach фактически использует итератор). Таким образом, любые преимущества в производительности должны быть доступны для обоих вариантов. Первая версия, конечно, не получит никаких преимуществ в производительности (например, если она Listбудет реализована в виде дерева, она будет работать медленнее).
MAK
36

Все они имеют собственное применение:

  1. Если у вас есть итерация, и вам нужно безоговорочно пройти ко всем из них:

    for (тип-итерации элемент-итерируемый: коллекция)

  2. Если у вас есть итерация, но вам нужно условно пройти:

    for (Итератор iterator = collection.iterator (); iterator.hasNext ();)

  3. Если структура данных не реализует итерацию:

    для (int i = 0; i <collection.length; i ++)

Сурадж Чандран
источник
Можете ли вы сделать это для условного обхода первого метода: if (iterable_element.some_attribute) {// что-то делать; } else {// ничего не делаем; }. Если да, то по существу нет никакой разницы между первым и вторым методом, кроме синтаксического сахара.
Thomas Nguyen
1 так же может условно перемещаться, как и другие, используя breakи / илиcontinue
arcyqwerty
13

В Java 8 также есть утилита stream () коллекций.

collection.forEach((temp) -> {
            System.out.println(temp);
});

или

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

Дополнительная информация о потоке Java 8 и коллекциях для чудес по ссылке

snr
источник
4

Ни один из них не «лучше» других. Третий вариант для меня более читабелен, но для тех, кто не использует foreache, это может показаться странным (они могут предпочесть первое). Все 3 довольно понятны любому, кто разбирается в Java, поэтому выберите то, что заставляет вас чувствовать себя лучше в коде.

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

Рэйф Кеттлер
источник
2

Первый вариант лучше по производительности (поскольку ArrayList реализует интерфейс RandomAccess). Согласно java-документу, реализация List должна реализовывать интерфейс RandomAccess, если для типичных экземпляров класса этот цикл:

 for (int i=0, n=list.size(); i < n; i++)
     list.get(i);

работает быстрее, чем этот цикл:

 for (Iterator i=list.iterator(); i.hasNext(); )
     i.next();

Я надеюсь, что это помогает. Первый вариант будет медленным для списков последовательного доступа.

Амитав Падхи
источник
1

Вот пример

Query query = em.createQuery("from Student");
             java.util.List list = query.getResultList();
             for (int i = 0; i < list.size(); i++) 
             {

                 student = (Student) list.get(i);
                 System.out.println(student.id  + "  " + student.age + " " + student.name + " " + student.prenom);

             }
Абделухаб
источник