Java: найкращий спосіб повторити колекцію (тут ArrayList)


98

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

Ітерація через колекцію (тут ArrayList)

Я чомусь переглянув параметри автоматичного завершення Eclipse, і мені стало цікаво:

У яких випадках краще використовувати наступні петлі, ніж інші?

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

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

Ітератор має наступний () / наступний ():

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
Інтерфейс колекції не містить методу "отримати", тому перший не завжди можливий.
ThePerson

Відповіді:


104

Перший корисний, коли вам також потрібен індекс елемента. Це в основному еквівалентно двом іншим варіантам для ArrayLists, але буде дуже повільним, якщо ви використовуєте a LinkedList.

Другий корисний, коли вам не потрібен індекс елемента, але, можливо, потрібно буде видалити елементи під час ітерації. Але це має недолік - бути занадто багатослівним ІМО.

Третя версія також є моїм бажаним вибором. Він короткий і працює для всіх випадків, коли вам не потрібні індекси або базовий ітератор (тобто ви лише отримуєте доступ до елементів, не видаляючи їх або Collectionжодним чином не змінюючи - що є найпоширенішим випадком).


3
+1 Побий мене до цього. збирався згадати LinkedList (не всі Collection дешеві для пошуку за індексом).
The Scrum Meister

Поки потрібні просто циклічні елементи, завжди добре використовувати 3-ю версію, оскільки реалізація вже існує для всіх різних колекцій, а Sun або будь-яка реалізація JDK прагне покращити продуктивність, а не кожну.
Фані

@Phani: Інші два варіанти також працюють на будь-якій реалізації JDK.
МАК

Я не казав, що це не спрацює, але якщо випадково внести певні покращення чи зміни продуктивності для реалізації колекцій, то це автоматично застосує ваш код і вам не потрібно писати для підвищення продуктивності це те, що я маю на увазі.
Фані

1
@ Фані: AFAIK 3-а форма - це просто синтаксичний цукор для 2-ї форми (тобто під кришкою варіант foreach використовує ітератор). Тому будь-які переваги від продуктивності повинні бути доступні для обох варіантів. Перша версія звичайно не отримає жодних переваг від продуктивності (наприклад, якщоList реалізовано як дерево, воно насправді буде повільніше).
МАК

36

Усі вони мають власні потреби:

  1. Якщо у вас є ітерабельний і вам потрібно беззастережно переходити до них усіх:

    для (iterable_type iterable_element: collection)

  2. Якщо у вас є ітерабельний, але вам потрібно умовно перейти:

    для (Iterator iterator = collection.iterator (); iterator.hasNext ();)

  3. Якщо структура даних не реалізує ітерабельно:

    for (int i = 0; i <collection.length; i ++)


Чи можете ви це зробити, щоб перемогти перший метод умовно: якщо (iterable_element.some_attribute) {// зробити щось; } else {// нічого не робити; }. Якщо так, то по суті немає різниці між першим та другим методом, крім синтаксичного цукру.
Томас Нгуен

1 настільки ж здатний умовно перейти, як інші, які використовують breakта / абоcontinue
arcyqwerty

13

Додатково використовується утиліта stream () колекцій з Java 8

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

або

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

Більш детальна інформація про Java 8 потоку і колекціям для wonderers посилання


4

Жоден з них не "кращий", ніж інші. Третє, для мене, читабельніше, але для того, хто не використовує передбачень, це може виглядати дивно (вони можуть віддати перевагу першому). Усі 3 досить зрозумілі для всіх, хто розуміє Java, тому вибирайте те, що робить вас краще щодо коду.

Перший - найосновніший, тому це найбільш універсальний зразок (працює для масивів, усіх ітерабелів, які я можу придумати). Це єдина різниця, яку я можу придумати. У більш складних випадках (наприклад, вам потрібно мати доступ до поточного індексу або вам потрібно відфільтрувати список), перший і другий випадки можуть мати більше сенсу відповідно. Для простого випадку (ітерабельний об'єкт, особливих вимог немає) третій видається найчистішим.


2

Перший варіант - краща ефективність роботи (Як ArrayList реалізує інтерфейс RandomAccess). Відповідно до java doc, реалізація списку повинна реалізовувати інтерфейс 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);

             }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.