Sposoby iteracji listy w Javie

Będąc nieco nowy w języku Java staram się zapoznać ze wszystkimi sposobami (a przynajmniej tymi Nie patologicznymi), które można iterować poprzez listę (lub być może inne kolekcje) oraz zaletami lub wadami każdego z nich.

Biorąc pod uwagę obiekt List<E> list, znam następujące sposoby pętli przez wszystkie elementy:

Podstawowe dla loop (oczywiście istnieją równoważne while / do while pętle również)

// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
    E element = list.get(i);
    // 1 - can call methods of element
    // 2 - can use 'i' to make index-based calls to methods of list

    // ...
}

Uwaga: Jako @amarse za iterację nad List s, ponieważ rzeczywista implementacja metoda get może nie być tak skuteczna jak w przypadku stosowania metody Iterator. Na przykład, LinkedList implementacje muszą przemierzać wszystkie elementy poprzedzające i, aby uzyskać i-ten element.

W powyższym przykładzie nie ma możliwości implementacji List do "Zachowaj swoje miejsce", aby uczynić przyszłe iteracje bardziej wydajnymi. Dla ArrayList to naprawdę nie ma znaczenia, ponieważ złożoność / koszt get jest stała czasu(O (1)), podczas gdy dla LinkedList jest proporcjonalna do wielkości listy(O (n)).

Aby uzyskać więcej informacji na temat złożoności obliczeniowej wbudowanych implementacji Collections, sprawdź to pytanie.

Enhanced for loop (ładnie Wyjaśnione w tym pytaniu)

for (E element : list) {
    // 1 - can call methods of element

    // ...
}

Iterator

for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list

    // ...
}

Listysterator

for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list
    // 3 - can use iter.add(...) to insert a new element into the list
    //     between element and iter->next()
    // 4 - can use iter.set(...) to replace the current element

    // ...
}

Funkcjonalne Java

list.stream().map(e -> e + 1); // Can apply a transformation function for e

Iterable.forEach, strumień.forEach ,...

(metoda map z API strumienia Java 8 (Zobacz odpowiedź @i_am_zero).)

W Javie 8 klas kolekcji implementujących Iterable (na przykład wszystkie List s) mają teraz metodę forEach, która może być używana zamiast instrukcji for loop pokazanej powyżej. (Oto kolejne pytanie, które zapewnia dobre porównanie.)

Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
//     (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
//     being performed with each item.

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).

Jakie są inne sposoby, jeśli jakieś?

(BTW, moje zainteresowanie wcale nie wynika z chęci optymalizacji wydajności; chcę tylko wiedzieć, jakie formularze są dostępne dla mnie jako programisty.)

Author: Peter Mortensen, 2013-08-23

11 answers

[14]}trzy formy pętli są prawie identyczne. Rozszerzona pętla for:

for (E element : list) {
    . . .
}

Jest, zgodnie ze specyfikacją języka Java , identyczny w efekcie do jawnego użycia iteratora z tradycyjną pętlą for. W trzecim przypadku można modyfikować zawartość listy tylko przez usunięcie bieżącego elementu, a następnie tylko wtedy, gdy odbywa się to za pomocą metody remove samego iteratora. Dzięki iteracji opartej na indeksach możesz dowolnie modyfikować listę w dowolnym sposób. Jednak dodawanie lub usuwanie elementów przed bieżącym indeksem grozi, że Twoja pętla pominie elementy lub przetworzy ten sam element wiele razy; musisz odpowiednio dostosować indeks pętli podczas wprowadzania takich zmian.

We wszystkich przypadkach, element jest odniesieniem do rzeczywistego elementu listy. Żadna z metod iteracji nie tworzy kopii czegokolwiek z listy. Zmiany stanu wewnętrznego element będą zawsze widoczne w stanie wewnętrznym odpowiedniego elementu na lista.

Zasadniczo istnieją tylko dwa sposoby iteracji listy: używając indeksu lub używając iteratora. Pętla enhanced for jest tylko skrótem składniowym wprowadzonym w Javie 5, aby uniknąć nudy jawnego definiowania iteratora. Dla obu stylów można wymyślić zasadniczo trywialne odmiany za pomocą for, while albo do while bloki, ale wszystkie sprowadzają się do tego samego (a raczej dwóch rzeczy).

EDIT: jak @iX3 wskazuje w komentarzu, możesz użyć ListIterator, aby ustawić bieżący element listy w trakcie iteracji. Trzeba by użyć List#listIterator() zamiast List#iterator() aby zainicjalizować zmienną pętli (która oczywiście musiałaby być zadeklarowana jako ListIterator, a nie Iterator).

 215
Author: Ted Hopp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-15 21:01:52

Przykład każdego rodzaju wymienionego w pytaniu:

/ Align = "left" / java
import java.util.*;

public class ListIterationExample {

     public static void main(String []args){
        List<Integer> numbers = new ArrayList<Integer>();

        // populates list with initial values
        for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7))
            numbers.add(i);
        printList(numbers);         // 0,1,2,3,4,5,6,7

        // replaces each element with twice its value
        for (int index=0; index < numbers.size(); index++) {
            numbers.set(index, numbers.get(index)*2); 
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // does nothing because list is not being changed
        for (Integer number : numbers) {
            number++; // number = new Integer(number+1);
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14  

        // same as above -- just different syntax
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            number++;
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // ListIterator<?> provides an "add" method to insert elements
        // between the current element and the cursor
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.add(number+1);     // insert a number right before this
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

        // Iterator<?> provides a "remove" method to delete elements
        // between the current element and the cursor
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            if (number % 2 == 0)    // if number is even 
                iter.remove();      // remove it from the collection
        }
        printList(numbers);         // 1,3,5,7,9,11,13,15

        // ListIterator<?> provides a "set" method to replace elements
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.set(number/2);     // divide each element by 2
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7
     }

     public static void printList(List<Integer> numbers) {
        StringBuilder sb = new StringBuilder();
        for (Integer number : numbers) {
            sb.append(number);
            sb.append(",");
        }
        sb.deleteCharAt(sb.length()-1); // remove trailing comma
        System.out.println(sb.toString());
     }
}
 39
Author: iX3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-23 21:09:38

Pętla podstawowa nie jest zalecana, ponieważ nie znasz implementacji listy.

Jeśli to była LinkedList, każde wywołanie do

list.get(i)

Byłoby iteracją nad listą, co skutkowałoby złożonością czasową N^2.

 19
Author: amarseillan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-08 19:22:19

Iteracja w stylu JDK8:

public class IterationDemo {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3);
        list.stream().forEach(elem -> System.out.println("element " + elem));
    }
}
 15
Author: eugene82,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-06 17:01:47

W Java 8 mamy wiele sposobów iteracji klas kolekcji.

Using Iterable forEach

Zbiory implementujące Iterable (na przykład wszystkie listy) mają teraz metodę forEach. Możemy użyć metody - reference wprowadzonej w Javie 8.

Arrays.asList(1,2,3,4).forEach(System.out::println);

Używanie strumieni forEach i forEachOrdered

Możemy również iterować nad listą używając Stream jako:

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);

Powinniśmy preferować forEachOrdered nad forEach, ponieważ zachowanie forEach jest jawnie nieeterministyczne, gdzie jako forEachOrdered wykonuje akcję dla każdego elementu tego strumienia, w porządku spotkania strumienia, jeśli strumień ma zdefiniowany porządek spotkania. Tak więc forEach nie gwarantuje, że zamówienie zostanie zachowane.

Zaletą strumieni jest to, że możemy również korzystać z równoległych strumieni, gdzie jest to właściwe. Jeśli celem jest tylko wydrukowanie elementów niezależnie od kolejności, możemy użyć parallel stream jako:

Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);
 5
Author: i_am_zero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-25 02:08:09

Nie wiem, co uważasz za patologiczne, ale pozwól mi podać kilka alternatyw, których wcześniej nie widziałeś: {]}

List<E> sl= list ;
while( ! sl.empty() ) {
    E element= sl.get(0) ;
    .....
    sl= sl.subList(1,sl.size());
}

Lub jego rekurencyjna wersja:

void visit(List<E> list) {
    if( list.isEmpty() ) return;
    E element= list.get(0) ;
    ....
    visit(list.subList(1,list.size()));
}

Również rekurencyjna wersja klasycznego for(int i=0...:

void visit(List<E> list,int pos) {
    if( pos >= list.size() ) return;
    E element= list.get(pos) ;
    ....
    visit(list,pos+1);
}

Wspominam o nich, ponieważ jesteś "trochę nowy w Javie" i to może być interesujące.

 4
Author: Mario Rossi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-23 19:59:29

Możesz użyć forEach zaczynając od Java 8:

 List<String> nameList   = new ArrayList<>(
            Arrays.asList("USA", "USSR", "UK"));

 nameList.forEach((v) -> System.out.println(v));
 2
Author: Sudip Bhandari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-23 17:28:03

Do wyszukiwania wstecz powinieneś użyć następującego:

for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {
    SomeClass item = iterator.previous();
    ...
    item.remove(); // For instance.
}

Jeśli chcesz poznać pozycję, Użyj iteratora.previousIndex (). Pomaga również napisać wewnętrzną pętlę, która porównuje dwie pozycje na liście (Iteratory nie są sobie równe).

 0
Author: CoolMind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-25 13:12:18

W java 8 możesz użyć metody List.forEach() z lambda expression do iteracji listy.

import java.util.ArrayList;
import java.util.List;

public class TestA {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("Apple");
        list.add("Orange");
        list.add("Banana");
        list.forEach(
                (name) -> {
                    System.out.println(name);
                }
        );
    }
}
 0
Author: pippi longstocking,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-06 13:34:26

Prawo, wiele alternatyw są wymienione. Najprostszym i najczystszym byłoby użycie rozszerzonej instrukcji for, Jak Poniżej. {[3] } jest typu iteracyjnego.

for ( FormalParameter : Expression ) Statement

Na przykład, aby iterować, List ids, możemy po prostu tak,

for (String str : ids) {
    // Do something
}
 -1
Author: Yu Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-23 17:26:33

Zawsze możesz wyłączyć pierwszy i trzeci przykład za pomocą pętli while I trochę więcej kodu. Daje to przewagę możliwości użycia Do-while:

int i = 0;
do{
 E element = list.get(i);
 i++;
}
while (i < list.size());

Oczywiście, tego rodzaju rzeczy mogą spowodować wyjątek NullPointerException, jeśli lista.size() zwraca 0, ponieważ zawsze jest wykonywana co najmniej raz. To może być naprawione przez testowanie czy element jest null przed użyciem jego atrybutów / metod tho. Jednak o wiele prostsze i łatwiejsze jest użycie pętli for

 -2
Author: shieldgenerator7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-23 19:07:36