web-dev-qa-db-ja.com

Javaでリストを反復処理する方法

Java言語にやや慣れていない私は、リスト(あるいはおそらく他のコレクション)を通して繰り返すことができるすべての方法(あるいは少なくとも非病理的な方法)とそれぞれの長所と短所をよく理解しようとしています。

List<E> listオブジェクトがあれば、私はすべての要素をループ処理するための以下の方法を知っています。

基本的な forloop (もちろん、同等の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が指摘したように、Listメソッドの実際の実装はgetを使用する場合ほど効率的ではない可能性があるため、この形式はIteratorsの繰り返し処理には適していません。たとえば、LinkedList実装は、i番目の要素を取得するために、iに先行するすべての要素をトラバースする必要があります。

上記の例では、List実装が将来の繰り返しをより効率的にするために「場所を保存する」ことはできません。 ArrayListの場合、リストのサイズ(O(n))に比例するのに対し、getの複雑さ/コストは一定の時間(O(1))なので、LinkedListの場合は特に重要ではありません。

組み込みのCollections実装の計算上の複雑さについての詳細は、 this question をご覧ください。

拡張された 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

    // ...
}

機能的Java

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

Iterable.forEachStream.forEach 、...

(Java 8のStream APIからのmapメソッド(@ i_am_zeroの回答を参照)

Iterableを実装するJava 8コレクションクラス(たとえば、すべての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).

他にどのような方法がありますか?

(ところで、私の興味は パフォーマンスの最適化 を望んでいることに全く由来していません;私は開発者として私に利用可能なフォームを知りたいだけです。)

516
iX3

3種類のループはほぼ同じです。強化されたforループ

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

これは、 Java言語仕様 同一 に従って、従来のforループでイテレータを明示的に使用した場合と同じです。 3番目のケースでは、現在の要素を削除することによってのみリストの内容を変更することができます。その場合は、イテレータ自体のremoveメソッドを介してそれを実行する場合に限られます。インデックスベースの反復では、リストを自由に変更できます。ただし、現在のインデックスより前の要素を追加または削除すると、ループで要素がスキップされたり、同じ要素が複数回処理されたりする可能性があります。そのような変更を行うときは、ループインデックスを正しく調整する必要があります。

すべての場合において、elementは実際のリスト要素への参照です。どの反復方法も、リスト内のもののコピーを作成しません。 elementの内部状態への変更は常にリストの対応する要素の内部状態で見られます。

基本的に、リストを反復処理するには、インデックスを使用する方法と反復子を使用する方法の2つしかありません。拡張forループは、反復子を明示的に定義するという面倒を避けるためにJava 5で導入された単なる構文上のショートカットです。どちらのスタイルでも、forwhiledo whileブロックを使用して、本質的には簡単なバリエーションを思いつくことができますが、それらはすべて同じもの(または2つのもの)にまとめられています。

編集:@ iX3がコメントで指摘しているように、繰り返している間、ListIteratorを使ってリストの現在の要素を設定することができます。ループ変数を初期化するには、 List#listIterator() の代わりに List#iterator() を使用する必要があります(当然、ListIteratorではなくIteratorを宣言する必要があります)。

234
Ted Hopp

質問にリストされている各種類の例:

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());
     }
}
40
iX3

リストの実装がわからないため、基本的なループはお勧めできません。

それがLinkedListの場合、を呼び出すたびに

list.get(i)

リストを反復処理することになり、結果的にN ^ 2倍の複雑さになります。

20
amarseillan

JDK 8スタイルの反復:

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

Java 8 では、コレクションクラスを反復処理する方法が複数あります。

繰り返し可能なforEachを使う

Iterablename__を実装するコレクション(すべてのリストなど)にforEachname__メソッドが追加されました。 Java 8で導入された method-reference を使用できます。

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

Stream forEachおよびforEachOrderedの使用

Stream を使用してリストを反復処理することもできます。

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

forEachOrderedname__の振る舞いは明示的に非決定的であるため、forEachname__はこのストリームの各要素に対してアクションを実行するため、ストリームに遭遇順が定義されている場合は、forEachname__をforEachOrderedname__より優先する必要があります。そのため、forEachは注文が保持されることを保証しません。

ストリームの利点は、適切な場合にはパラレルストリームも利用できることです。目的が順序に関係なくアイテムを印刷することだけである場合、パラレルストリームを次のように使用できます。

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

私はあなたが病理学的に考えるものを知らないが、あなたが前に見たことがなかったかもしれないいくつかの選択肢を提供させてください:

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

またはその再帰的バージョン:

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

また、古典的な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);
}

あなたが「ややJavaに慣れていない」ので、私はそれらに言及します、そしてこれは面白いかもしれません。

4
Mario Rossi

Java 8から forEach を使用できます。

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

 nameList.forEach((v) -> System.out.println(v));
2
Sudip Bhandari

後方検索の場合は、次のものを使用してください。

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

位置を知りたい場合は、iterator.previousIndex()を使用してください。また、リスト内の2つの位置を比較する内部ループを作成するのに役立ちます(反復子は等しくありません)。

0
CoolMind

Java 8では、lambda expressionとともにList.forEach()メソッドを使用して、リストを繰り返すことができます。

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