Javaでfor eachループを逆の順序で実行できますか?


148

Javaを使用して逆の順序でリストを実行する必要があります。

これが転送する場所:

for(String string: stringList){
//...do something
}

for each構文を使用して逆の順序でstringListを反復する方法はありますか?

わかりやすくするために、私はリストを逆の順序で反復する方法を知っていますが、(好奇心のために)for eachスタイルでそれを行う方法を知りたいです。


5
「for-each」ループのポイントは、各要素に対して操作を実行するだけでよく、順序は重要ではないということです。for-eachは要素を完全にランダムな順序で処理することができ、それでも設計された目的を実行します。特定の方法で要素を処理する必要がある場合は、手動で処理することをお勧めします。
muusbolla 2009

Javaコレクションライブラリ。言語とは関係ありません。ジョシュ・ブロッホのせいにして。
トム・ホーティン-タックライン2009

7
@muusbolla:しかし、リストは順序付けられたコレクションなので、その順序は確かに尊敬されますか?したがって、for-eachはリストの要素をランダムな順序で処理しません。
Lee Kowalkowski、2011年

5
それは真実ではない@muusbolla。たぶん、Set派生コレクションの場合です。コレクションのメソッドforeachから返された反復子の順序での反復を保証しますiterator()docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

回答:


151

Collections.reverseメソッドは実際には、元のリストの要素が逆の順序でコピーされた新しいリストを返すため、元のリストのサイズに関してO(n)のパフォーマンスが得られます。

より効率的なソリューションとして、Listの逆のビューをIterableとして表示するデコレーターを作成できます。デコレーターによって返されるイテレーターは、装飾されたリストのListIteratorを使用して、要素を逆の順序で処理します。

例えば:

public class Reversed<T> implements Iterable<T> {
    private final List<T> original;

    public Reversed(List<T> original) {
        this.original = original;
    }

    public Iterator<T> iterator() {
        final ListIterator<T> i = original.listIterator(original.size());

        return new Iterator<T>() {
            public boolean hasNext() { return i.hasPrevious(); }
            public T next() { return i.previous(); }
            public void remove() { i.remove(); }
        };
    }

    public static <T> Reversed<T> reversed(List<T> original) {
        return new Reversed<T>(original);
    }
}

そしてあなたはそれを次のように使うでしょう:

import static Reversed.reversed;

...

List<String> someStrings = getSomeStrings();
for (String s : reversed(someStrings)) {
    doSomethingWith(s);
}

22
これが基本的にGoogleのIterables.reverseが行うことです。そうです:)
ジョンスキート

10
私はジョンの答えを受け入れる必要があるという「ルール」があることを知っています:) ..これを受け入れたいのです(たとえそれらが本質的に同じであっても)私は別のサードパーティライブラリを含める必要がないので(たとえ一部の人は、その理由がOOの主な利点の1つである再利用性を壊すと主張するかもしれません。
Ron Tuffin 2009

小さなエラー:public void remove()では、returnステートメントがあってはなりません。それは次のとおりです:i.remove();
Jesper、

10
Collections.reverse()は反転されたコピーを返しませんが、パラメーターとして渡されたリストに作用します。ただし、イテレータを使用したソリューションと同じです。本当にエレガント。
er4z0r

96

リストについては、Google Guava Libraryを使用できます。

for (String item : Lists.reverse(stringList))
{
    // ...
}

これ、コレクション全体を逆にしたり、そのようなことをしたりするわけではないことに注意しください。逆の順序で反復とランダムアクセスを許可するだけです。これは、最初にコレクションを元に戻すよりも効率的です。Lists.reverse

任意の反復可能オブジェクトを元に戻すには、すべてを読み取ってから逆方向に「再生」する必要があります。

(あなたはすでにそれを使用していない場合、私は思い徹底的にあなたが見てい推奨しグアバを。それは素晴らしいものです。)


1
私たちのコードベースは、larvalabs(larvalabs.com/collections)によってリリースされたCommonsコレクションの汎用バージョンを多用しています。Apache CommonsのSVNリポジトリを見ると、Commons CollectionのJava 5バージョンをリリースする作業の大部分が行われていることが明らかですが、まだリリースされていません。
skaffman 2009

私はそれが好きです。それがあまり役に立たなかったら、私はそれをプラグと呼びます。
geowa4 2009

なぜジャカルタがApache Commonsを更新する気にならなかったのかしら。
ウリ

3
彼らはそれを更新しました、それが私が言っていることです。彼らはそれをリリースしていません。
skaffman 2009

23
Iterables.reverseは非推奨になりました。代わりにLists.reverseまたはImmutableList.reverseを使用してください。
Garrett Hall、

39

リストは(セットとは異なり)順序付けられたコレクションであり、リストを反復処理すると、契約によって順序が保持されます。スタックが逆の順序で反復することを期待していましたが、残念ながらそうではありません。だから私が考えることができる最も簡単な解決策はこれです:

for (int i = stack.size() - 1; i >= 0; i--) {
    System.out.println(stack.get(i));
}

これは「for each」ループソリューションではないことを理解しています。Googleコレクションのような新しいライブラリを導入するよりも、forループを使用したいと思います。

Collections.reverse()もその役割を果たしますが、逆の順序でコピーを返すのではなく、リストを更新します。


3
このアプローチは、配列ベースのリスト(ArrayListなど)では問題ないかもしれませんが、リンクされたリストの場合、各getが各getでリストを最初から最後(または場合によっては最後から最初)にトラバースする必要があるため、最適ではありません。Natのソリューションのように、よりスマートなイテレータを使用することをお勧めします(リストのすべての実装に最適)。
クリス

1
さらに、for each構文を明示的に要求するOPの要求から逸脱している
Paul W

8

これは元のリストを混乱させ、ループの外で呼び出す必要もあります。また、ループするたびに逆を実行する必要はありません-いずれIterables.reverse ideasかが適用された場合、それは本当ですか

Collections.reverse(stringList);

for(String string: stringList){
//...do something
}

5

私の知る限り、標準ライブラリには、for-each構文をサポートする標準的な「reverse_iterator」のようなものはありません。for-each構文は、すでに言語に導入された構文糖衣です。

for(Item element:myList.clone()。reverse())のようなことをして、関連する価格を支払うことができます。

これはまた、高価な操作を実行する便利な方法を提供しないという明らかな現象とかなり一貫しているようです-リストは、定義により、O(N)ランダムアクセスの複雑さを持つ可能性があるため(単一リンクでインターフェイスを実装できます)、逆反復は最終的にO(N ^ 2)になる可能性があります。もちろん、ArrayListがある場合、その価格を支払う必要はありません。


ListIteratorを逆方向に実行できます。これは、Iterator内でラップできます。
トム・ホーティン-タックライン2009

@トム:良い点。ただし、イテレータを使用しても、迷惑な古いスタイルのforループを実行しているため、最初の最後の要素に到達するためのコストを支払う可能性があります。
ウリ

Dequeには逆反復子があります。
マイケルマンゼイ2010

2

これはオプションの場合があります。最後の要素から開始するには、whileループを最後まで行うよりも良い方法があることを願っています。

public static void main(String[] args) {        
    List<String> a = new ArrayList<String>();
    a.add("1");a.add("2");a.add("3");a.add("4");a.add("5");

    ListIterator<String> aIter=a.listIterator();        
    while(aIter.hasNext()) aIter.next();

    for (;aIter.hasPrevious();)
    {
        String aVal = aIter.previous();
        System.out.println(aVal);           
    }
}


1

要素を逆にする列挙子を提供するカスタムコードを書くことなしにはできません。

要素を逆の順序で返すIterableのカスタム実装を作成することで、Javaでそれを実行できるはずです。

次に、ラッパーをインスタンス化(またはメソッドwhat-have-youを呼び出し)し、forループ内の要素を逆にするIterable実装を返します。



1

for each構文をそのまま使用して逆の順序で進みたい場合は、コレクションを逆にする必要があります。


1

上記のすべての回答は、別のメソッドをラップするか、外部のいくつかの外部コードを呼び出すことによってのみ、要件を満たします。

Thinking in Java 4thエディションの11.13.1章「AdapterMethodIdiom」からコピーしたソリューションを次に示します。

これがコードです:

// The "Adapter Method" idiom allows you to use foreach
// with additional kinds of Iterables.
package holding;
import java.util.*;

@SuppressWarnings("serial")
class ReversibleArrayList<T> extends ArrayList<T> {
  public ReversibleArrayList(Collection<T> c) { super(c); }
  public Iterable<T> reversed() {
    return new Iterable<T>() {
      public Iterator<T> iterator() {
        return new Iterator<T>() {
          int current = size() - 1; //why this.size() or super.size() wrong?
          public boolean hasNext() { return current > -1; }
          public T next() { return get(current--); }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
    };
  }
}   

public class AdapterMethodIdiom {
  public static void main(String[] args) {
    ReversibleArrayList<String> ral =
      new ReversibleArrayList<String>(
        Arrays.asList("To be or not to be".split(" ")));
    // Grabs the ordinary iterator via iterator():
    for(String s : ral)
      System.out.print(s + " ");
    System.out.println();
    // Hand it the Iterable of your choice
    for(String s : ral.reversed())
      System.out.print(s + " ");
  }
} /* Output:
To be or not to be
be to not or be To
*///:~

なぜint current = size() - 1正しいのですか?なぜないint current = this.size() - 1int current = super.size() - 1
qiwenリチウム

1

回避策:

Collections.reverse(stringList).forEach(str -> ...);

またはグアバと

Lists.reverse(stringList).forEach(str -> ...);

0

間違いなくこの質問への遅い答え。1つの可能性は、forループでListIteratorを使用することです。コロン構文ほどきれいではありませんが、機能します。

List<String> exampleList = new ArrayList<>();
exampleList.add("One");
exampleList.add("Two");
exampleList.add("Three");

//Forward iteration
for (String currentString : exampleList) {
    System.out.println(currentString); 
}

//Reverse iteration
for (ListIterator<String> itr = exampleList.listIterator(exampleList.size()); itr.hasPrevious(); /*no-op*/ ) {
    String currentString = itr.previous();
    System.out.println(currentString); 
}

ListIterator構文の功績は「Javaのリストを反復する方法」にあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.