天天看點

周遊Java中的清單的方法

本文翻譯自:Ways to iterate over a list in Java

Being somewhat new to the Java language I'm trying to familiarize myself with all the ways (or at least the non-pathological ones) that one might iterate through a list (or perhaps other collections) and the advantages or disadvantages of each.

對Java語言有些陌生,我試圖使自己熟悉所有可能周遊清單(或其他集合)的方式(或至少是非病理性方式)以及每種方式的優缺點。

Given a

List<E> list

object, I know of the following ways to loop through all elements:

給定一個

List<E> list

對象,我知道以下周遊所有元素的方式:

Basic for loop (of course, there're equivalent

while

/

do while

loops as well) 基本的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

    // ...
}
           

Note: As @amarseillan pointed out, this form is a poor choice for iterating over

List

s, because the actual implementation of the

get

method may not be as efficient as when using an

Iterator

.

注意:正如@a​​marseillan指出的那樣,這種形式對于疊代

List

是一個糟糕的選擇,因為

get

方法的實際實作可能不如使用

Iterator

時高效。

For example,

LinkedList

implementations must traverse all of the elements preceding i to get the i-th element.

例如,

LinkedList

實作必須周遊i之前的所有元素才能獲得第i個元素。

In the above example there's no way for the

List

implementation to "save its place" to make future iterations more efficient.

在上面的示例中,

List

實作無法“儲存位置”以使将來的疊代更加有效。

For an

ArrayList

it doesn't really matter, because the complexity/cost of

get

is constant time (O(1)) whereas for a

LinkedList

is it proportional to the size of the list (O(n)).

對于一個

ArrayList

它其實并不重要,因為複雜性/成本

get

是恒定的時間(O(1)),而對于

LinkedList

是成正比的清單的大小(為O(n))。

For more information about the computational complexity of the built-in

Collections

implementations, check out this question .

有關内置

Collections

實作的計算複雜度的更多資訊,請檢視此問題 。

Enhanced for loop (nicely explained in this question ) 增強了for循環 ( 此問題對此做了很好的解釋)

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

    // ...
}
           

ListIterator 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 功能性Java

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

Iterable.forEach , Stream.forEach , ... Iterable.forEach , Stream.forEach ,...

(A map method from Java 8's Stream API (see @i_am_zero's answer).)

(來自Java 8的Stream API的map方法(請參閱@i_am_zero的答案)。)

In Java 8 collection classes that implement

Iterable

(for example, all

List

s) now have a

forEach

method, which can be used instead of the for loop statement demonstrated above.

在實作

Iterable

Java 8集合類(例如,所有

List

)中,現在具有

forEach

方法,可以使用該方法代替上面示範的for循環語句 。

(Here is another question that provides a good comparison.)

(這是另一個可以很好比較的問題。)
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).
           

What other ways are there, if any?

還有什麼其他方式(如果有)?

(BTW, my interest does not stem at all from a desire to optimize performance ; I just want to know what forms are available to me as a developer.)

(順便說一句,我的興趣根本不是出于優化性能的願望;我隻是想知道開發人員可以使用哪些形式。)

#1樓

參考:https://stackoom.com/question/1FFI3/周遊Java中的清單的方法

#2樓

You could always switch out the first and third examples with a while loop and a little more code.

您總是可以使用while循環和更多的代碼切換出第一個和第三個示例。

This gives you the advantage of being able to use the do-while:

這為您提供了可以使用do-while的優勢:
int i = 0;
do{
 E element = list.get(i);
 i++;
}
while (i < list.size());
           

Of course, this kind of thing might cause a NullPointerException if the list.size() returns 0, becuase it always gets executed at least once.

當然,如果list.size()傳回0,則這種情況可能會導緻NullPointerException,因為它總是至少執行一次。

This can be fixed by testing if element is null before using its attributes / methods tho.

這可以通過在使用元素的屬性/方法之前測試元素是否為空來解決。

Still, it's a lot simpler and easier to use the for loop

盡管如此,使用for循環要簡單得多

#3樓

I don't know what you consider pathological, but let me provide some alternatives you could have not seen before:

我不知道您認為病理是什麼,但讓我提供一些您之前從未見過的選擇:
List<E> sl= list ;
while( ! sl.empty() ) {
    E element= sl.get(0) ;
    .....
    sl= sl.subList(1,sl.size());
}
           

Or its recursive version:

或其遞歸版本:
void visit(List<E> list) {
    if( list.isEmpty() ) return;
    E element= list.get(0) ;
    ....
    visit(list.subList(1,list.size()));
}
           

Also, a recursive version of the classical

for(int i=0...

:

另外,經典的

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

I mention them because you are "somewhat new to Java" and this could be interesting.

我之是以提到它們,是因為您“對Java有點陌生”,這可能很有趣。

#4樓

Example of each kind listed in the question:

問題中列出的每種示例:

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

#5樓

The three forms of looping are nearly identical.

三種形式的循環幾乎相同。

The enhanced

for

loop:

增強的

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.

根據Java語言規範 ,其作用與顯式使用帶有傳統

for

循環的疊代器相同 。

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.

在第三種情況下,您隻能通過删除目前元素來修改清單内容,然後再通過疊代器本身的

remove

方法來進行操作。

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.

進行此類更改時,您需要适當地調整循環索引。

In all cases,

element

is a reference to the actual list element.

在所有情況下,

element

都是對實際list元素的引用。

None of the iteration methods makes a copy of anything in the list.

沒有一種疊代方法可以複制清單中的任何内容。

Changes to the internal state of

element

will always be seen in the internal state of the corresponding element on the list.

element

内部狀态的更改将始終在清單中相應元素的内部狀态中顯示。

Essentially, there only two ways to iterate over a list: by using an index or by using an iterator.

本質上,隻有兩種方法可以周遊清單:使用索引或使用疊代器。

The enhanced for loop is just a syntactic shortcut introduced in Java 5 to avoid the tedium of explicitly defining an iterator.

增強的for循環隻是Java 5中引入的文法快捷方式,以避免顯式定義疊代器的繁瑣工作。

For both styles, you can come up with essentially trivial variations using

for

,

while

or

do while

blocks, but they all boil down to the same thing (or, rather, two things).

對于這兩種樣式,您都可以使用

for

while

do while

塊提供一些瑣碎的變體,但是它們全都歸結為同一件事(或者說是兩件事)。

EDIT: As @iX3 points out in a comment, you can use a

ListIterator

to set the current element of a list as you are iterating.

編輯:作為@ IX3在評論中指出,你可以使用

ListIterator

設定清單的目前元素為你疊代。

You would need to use

List#listIterator()

instead of

List#iterator()

to initialize the loop variable (which, obviously, would have to be declared a

ListIterator

rather than an

Iterator

).

您将需要使用

List#listIterator()

而不是

List#iterator()

來初始化循環變量(顯然,必須将其聲明為

ListIterator

而不是

Iterator

)。

#6樓

A JDK8-style iteration:

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