Java: najlepszy sposób na iterację w kolekcji (tutaj ArrayList)

98

Dzisiaj szczęśliwie kodowałem, gdy dotarłem do fragmentu kodu, którego użyłem już setki razy:

Iterowanie po kolekcji (tutaj ArrayList)

Z jakiegoś powodu przyjrzałem się opcjom autouzupełniania Eclipse i zacząłem się zastanawiać:

W jakich przypadkach poniższe pętle są lepsze w użyciu niż inne?

Klasyczna pętla indeksu tablicy:

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

Iterator hasNext () / next ():

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

I mój ulubiony, ponieważ jest tak prosty do napisania:

for (iterable_type iterable_element : collection) {

}
Jason Rogers
źródło
2
Jeśli chodzi o mnie, to najczęściej używam trzeciej pętli.
Harry Joy
1
Drugi sposób jest lepszy w użyciu:for (Iterator<type> iterator = collection.iterator(); iterator.hasNext();) { type type = iterator.next(); }
mike jones
4
Interfejs kolekcji nie zawiera metody „get”, więc pierwsza nie zawsze jest możliwa.
ThePerson

Odpowiedzi:

104

Pierwsza jest przydatna, gdy potrzebujesz również indeksu elementu. Jest to w zasadzie równoważne z pozostałymi dwoma wariantami dla ArrayLists, ale będzie bardzo powolne, jeśli użyjesz pliku LinkedList.

Drugi jest przydatny, gdy nie potrzebujesz indeksu elementu, ale może być konieczne usunięcie elementów podczas iteracji. Ale ma to tę wadę, że jest trochę zbyt rozwlekłe w IMO.

Trzecia wersja jest również moim ulubionym wyborem. Jest krótki i działa we wszystkich przypadkach, w których nie potrzebujesz żadnych indeksów ani bazowego iteratora (tj. Uzyskujesz dostęp tylko do elementów, nie usuwając ich ani nie modyfikując Collectionw jakikolwiek sposób - co jest najczęstszym przypadkiem).

MAK
źródło
3
+1 Pokonaj mnie. zamierzał wspomnieć o LinkedList (nie wszystkie Collection są tanie w wyszukiwaniu według indeksu).
The Scrum Meister
Tak długo, jak wymagane jest tylko zapętlenie elementów, zawsze dobrze jest użyć trzeciej wersji, ponieważ implementacja już istnieje dla wszystkich różnych kolekcji, a Sun lub dowolna implementacja JDK stara się poprawić wydajność, a nie każdą.
Phani
@Phani: Pozostałe dwa warianty również działają na dowolnej implementacji JDK.
MAK
Nie powiedziałem, że to nie zadziała, ale jeśli przez przypadek zostaną wprowadzone pewne ulepszenia lub zmiany wydajności dla implementacji kolekcji, to automatycznie zastosuje się to do twojego kodu i nie musisz pisać, aby poprawić wydajność o to mi chodzi.
Phani
1
@Phani: AFAIK trzecia forma jest po prostu cukrem syntaktycznym dla drugiej formy (tj. Pod maską każdy wariant faktycznie używa iteratora). Dlatego wszystkie korzyści dotyczące wydajności powinny być dostępne dla obu wariantów. Pierwsza wersja oczywiście nie przyniosłaby żadnych korzyści związanych z wydajnością (np. Gdyby Listzaimplementowano jako drzewo, w rzeczywistości byłoby wolniejsze).
MAK
36

Wszystkie mają tam swoje zastosowania:

  1. Jeśli masz element iterowalny i musisz bezwarunkowo przejść przez wszystkie z nich:

    for (iterable_type iterable_element: collection)

  2. Jeśli masz iterowalny, ale musisz warunkowo przejść:

    for (Iterator iterator = collection.iterator (); iterator.hasNext ();)

  3. Jeśli struktura danych nie implementuje iterowalnej:

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

Suraj Chandran
źródło
Czy możesz to zrobić, aby przejść przez pierwszą metodę warunkowo: if (iterable_element.some_attribute) {// zrób coś; } else {// nic nie rób; }. Jeśli tak, to zasadniczo nie ma różnicy między pierwszą a drugą metodą, poza cukrem syntaktycznym.
Thomas Nguyen
1 jest tak samo w stanie warunkowo przechodzić, jak inne, używając breaki / lubcontinue
arcyqwerty.
13

Istnieje dodatkowo funkcja stream () kolekcji w Javie 8

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

lub

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

Więcej informacji o strumieniu Java 8 i linku do kolekcji cudów

snr
źródło
4

Żaden z nich nie jest „lepszy” od pozostałych. Trzeci jest dla mnie bardziej czytelny, ale dla kogoś, kto nie korzysta z przesłuchań, może wyglądać dziwnie (może wolałby pierwszy). Wszystkie 3 są dość jasne dla każdego, kto rozumie język Java, więc wybierz tę, która sprawi, że poczujesz się lepiej z kodem.

Pierwszy jest najbardziej podstawowy, więc jest to najbardziej uniwersalny wzorzec (działa dla tablic, wszystkich iteracji, jakie przychodzą mi do głowy). To jedyna różnica, jaką przychodzi mi do głowy. W bardziej skomplikowanych przypadkach (np. Trzeba mieć dostęp do bieżącego indeksu lub przefiltrować listę), odpowiednio pierwszy i drugi przypadek mogą mieć większy sens. Dla prostego przypadku (obiekt iterowalny, bez specjalnych wymagań) trzeci wydaje się najczystszy.

Rafe Kettler
źródło
2

Pierwsza opcja jest lepsza pod względem wydajności (ponieważ ArrayList implementuje interfejs RandomAccess). Zgodnie z dokumentacją java, implementacja List powinna implementować interfejs RandomAccess, jeśli dla typowych instancji klasy pętla:

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

działa szybciej niż ta pętla:

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

Mam nadzieję, że to pomoże. Pierwsza opcja byłaby wolna dla sekwencyjnych list dostępu.

Amitav Padhi
źródło
1

Oto przykład

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);

             }
Abdelouhab
źródło