作为Java语言的新手,我试图熟悉遍历列表(或其他集合)的所有方法(或至少是非病态的方法)以及每种方法的优缺点。

给定List<E>列表对象,我知道以下方法来遍历所有元素:

基本的for循环(当然,也有等效的while / do while循环)

// 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

    // ...
}

注意:正如@amarseillan指出的,这种形式是一个糟糕的选择 用于迭代列表,因为的实际实现 get方法可能没有使用Iterator时那么有效。 例如,LinkedList实现必须遍历所有的 在I之前的元素得到第I个元素。

在上面的例子中,List实现没有办法 “保存其位置”以使未来的迭代更有效。 对于数组列表来说,这并不重要,因为get的复杂度/代价是常数时间(O(1)),而对于LinkedList,它与列表的大小(O(n))成正比。

有关内置Collections实现的计算复杂性的更多信息,请查看此问题。

增强的for循环(在这个问题中得到了很好的解释)

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

    // ...
}

迭代器

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

    // ...
}

ListIterator

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

    // ...
}

Functional Java

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

可迭代的forEach,流。forEach,……

(一个来自Java 8的流API的映射方法(参见@i_am_zero的答案)。)

在Java 8中实现Iterable的集合类(例如,all Lists)现在有一个forEach方法,可以用来代替上面演示的for循环语句。(这是另一个很好的比较问题。)

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

如果有的话,还有其他办法吗?

(顺便说一下,我的兴趣根本不是出于优化性能的愿望;我只是想知道作为开发人员,我有哪些表单可以使用。)


当前回答

循环的三种形式几乎相同。增强的for循环:

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

is, according to the Java Language Specification, identical in effect to the explicit use of an iterator with a traditional for loop. In the third case, you can only modify the list contents by removing the current element and, then, only if you do it through the remove method of the iterator itself. With index-based iteration, you are free to modify the list in any way. However, adding or removing elements that come before the current index risks having your loop skipping elements or processing the same element multiple times; you need to adjust the loop index properly when you make such changes.

在所有情况下,element都是对实际列表元素的引用。所有迭代方法都不会复制列表中的任何内容。元素内部状态的更改将始终在列表中相应元素的内部状态中看到。

本质上,只有两种方法可以遍历列表:使用索引或使用迭代器。增强的for循环只是Java 5中引入的语法快捷方式,以避免显式定义迭代器的单调乏味。对于这两种风格,你可以使用For、while或do while块想出本质上微不足道的变化,但它们都归结为同一件事(或者更确切地说,两件事)。

编辑:正如@iX3在评论中指出的那样,可以在迭代时使用ListIterator来设置列表的当前元素。您需要使用List#listIterator()而不是List#iterator()来初始化循环变量(显然,必须声明为listIterator而不是iterator)。

其他回答

在Java 8中,我们有多种方法来迭代集合类。

使用Iterable forEach

实现Iterable的集合(例如所有列表)现在有forEach方法。我们可以使用Java 8中引入的方法引用。

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

使用forEach和forEachOrdered流

我们还可以使用Stream as迭代列表:

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

我们应该更喜欢forEachOrdered而不是forEach,因为forEach的行为是显式的不确定的,其中forEachOrdered为该流的每个元素执行一个操作,如果流具有定义的遇到顺序,则按照流的遇到顺序。因此forEach并不保证顺序会被保持。

流的优点是我们还可以在适当的地方使用并行流。如果目标只是打印项目而不考虑顺序,那么我们可以使用并行流如下:

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

循环的三种形式几乎相同。增强的for循环:

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

is, according to the Java Language Specification, identical in effect to the explicit use of an iterator with a traditional for loop. In the third case, you can only modify the list contents by removing the current element and, then, only if you do it through the remove method of the iterator itself. With index-based iteration, you are free to modify the list in any way. However, adding or removing elements that come before the current index risks having your loop skipping elements or processing the same element multiple times; you need to adjust the loop index properly when you make such changes.

在所有情况下,element都是对实际列表元素的引用。所有迭代方法都不会复制列表中的任何内容。元素内部状态的更改将始终在列表中相应元素的内部状态中看到。

本质上,只有两种方法可以遍历列表:使用索引或使用迭代器。增强的for循环只是Java 5中引入的语法快捷方式,以避免显式定义迭代器的单调乏味。对于这两种风格,你可以使用For、while或do while块想出本质上微不足道的变化,但它们都归结为同一件事(或者更确切地说,两件事)。

编辑:正如@iX3在评论中指出的那样,可以在迭代时使用ListIterator来设置列表的当前元素。您需要使用List#listIterator()而不是List#iterator()来初始化循环变量(显然,必须声明为listIterator而不是iterator)。

上面你会发现所有迭代LIST的不同方法。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class test1 {

public static void main(String[] args) {
    //******* Exercise 1 : Write a Java program to create a new array list, add some colors (string) and print out the collection.
    List<String> colors = new ArrayList<String>();
    colors.add("Black");
    colors.add("Red");
    colors.add("Green");
    colors.add("Blue");
    System.out.println(colors);
    
    
    //******* Exercise 2 : Write a Java program to iterate through all elements in a array list. 
    System.out.println("//******* Exercise 2");
    List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    
    // iteration type 1 : using FOR loop
    System.out.println("// iteration type 1");
    for(Integer nb : list2) {
        System.out.print(nb + ", ");
    }
    System.out.println("\n");
    
    // iteration type 2 : using FOR loop
    System.out.println("// iteration type 2");
    for(int i=0; i < list2.size(); i++) {
        System.out.print(list2.get(i) + ", ");
    }System.out.println("\n");
    
    // iteration type 3  : using Do-While loop
    System.out.println("// iteration type 3");
    int index21 = 0;
    
    do {
        System.out.print(list2.get(index21) + ", ");
        index21++;
    }while(index21<list2.size());
    System.out.println("\n");
    
    
    // iteration type 4  : using While loop
    System.out.println("// iteration type 4");
    int index22 = 0;
    while(index22<list2.size()) {
        System.out.print(list2.get(index22) + ", ");
        index22++;
    }

    System.out.println("\n");
    
    
    // iteration type 5  : using  Iterable forEach loop 
    System.out.println("// iteration type 5");
     list2.forEach(elt -> {
         System.out.print(elt + ", ");
     });

    System.out.println("\n");
    
    
    // iteration type 6  : using  Iterator
    System.out.println("// iteration type 6");
    Iterator<Integer> listIterator = list2.iterator();
    while(listIterator.hasNext()) {
        System.out.print( listIterator.next() + ", ");
    }
    
    System.out.println("\n");
    
    // iteration type 7  : using  Iterator (From the beginning)
    System.out.println("// iteration type 7");
    ListIterator<Integer> listIterator21 = list2.listIterator(list2.size());
    while(listIterator21.hasPrevious()) {
        System.out.print( listIterator21.previous() + ", ");
    }

    System.out.println("\n");   
    
    // iteration type 8  : using  Iterator (From the End)
    System.out.println("// iteration type 8");
    ListIterator<Integer> listIterator22 = list2.listIterator();
    while(listIterator22.hasNext()) {
        System.out.print( listIterator22.next() + ", ");
    }

    System.out.println("\n");   
}

}

问题中列出的各种例子:

ListIterationExample.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());
     }
}

是的,列出了很多替代方案。最简单和最干净的方法是使用增强的for语句,如下所示。表达式是某种可迭代的类型。

for ( FormalParameter : Expression ) Statement

例如,要遍历List<String> ids,我们可以简单地这样,

for (String str : ids) {
    // Do something
}