Java의 각 루프마다 역순으로 할 수 있습니까?


148

Java를 사용하여 List를 역순으로 실행해야합니다.

그래서 이것이 전달되는 곳 :

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

구문 에 대해 를 사용하여 stringList를 역순으로 반복하는 방법이 있습니까?

명확성을 위해 : 목록을 역순으로 반복하는 방법을 알고 있지만 (호기심을 위해) 스타일 에 대해 목록을 수행하는 방법을 알고 싶습니다 .


5
"for-each"루프의 요점은 각 요소에 대해 작업을 수행하면되며 순서는 중요하지 않다는 것입니다. For-each는 요소를 완전히 임의의 순서로 처리 할 수 ​​있으며 원래 의도 한대로 수행 할 수 있습니다. 특정 방식으로 요소를 처리해야하는 경우 수동으로 수행하는 것이 좋습니다.
muusbolla

자바 컬렉션 라이브러리. 언어와 관련이 없습니다. 조쉬 블로흐를 비난하십시오.
Tom Hawtin-tackline

7
@ muusbolla : 그러나 List는 주문 모음이므로 반드시 주문이 존중 될 것입니까? 따라서 for-each는 List의 요소를 임의의 순서로 처리하지 않습니다.
Lee Kowalkowski

5
@muusbolla 사실이 아닙니다. 아마도 Set파생 컬렉션 의 경우 일 것입니다 . 콜렉션 foreachiterator()메소드 에서 리턴 된 반복자의 순서로 반복을 보장합니다 . docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

답변:


151

Collections.reverse 메소드는 실제로 원래 목록의 요소가 역순으로 복사 된 새 목록을 리턴하므로 원래 목록의 크기와 관련하여 O (n) 성능을 갖습니다.

보다 효율적인 솔루션으로 목록의 반전 된보기를 반복 가능으로 표시하는 데코레이터를 작성할 수 있습니다. 데코레이터가 반환 한 반복자는 데코 레이팅 된 목록의 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가하는 일입니다. 예 :)
Jon Skeet

10
나는 우리가 Jon의 대답을 받아 들여야하는 '규칙'이 있다는 것을 알고 있습니다. 일부는 그 이유가 OO의 재사용 가능한 장점 중 하나를 위반한다고 주장 할 수있다).
Ron Tuffin

작은 오류 : public void remove ()에는 return 문이 없어야합니다. i.remove ();
Jesper

10
Collections.reverse ()는 역전 된 사본을 반환하지 않지만 매개 변수로 전달 된 List에서 작동합니다. 그러나 반복자를 사용하는 솔루션처럼. 정말 우아합니다.
er4z0r

96

목록을 보려면 Google Guava Library를 사용할 수 있습니다 .

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

즉 참고 하지 않습니다Lists.reverse 전체 수집을 반대하거나 같은 것을 할 수는 - 그냥 역순으로, 반복 및 랜덤 액세스 할 수 있습니다. 이것은 컬렉션을 먼저 되 돌리는 것보다 효율적입니다.

임의의 이터 러블을 뒤집으려면, 모든 것을 읽고 나서 거꾸로 "재생"해야합니다.

(당신이 이미 그것을 사용하지 않는 경우, 나는 것 철저하게 당신이 한 번 봐 가지고 추천 구아바를 . 그것은 좋은 물건입니다.)


1
우리의 코드베이스는 larvalabs ( larvalabs.com/collections )에서 공개 한 Commons Collections의 생성 된 버전을 많이 사용합니다 . Apache Commons의 SVN 저장소를 살펴보면 Commons Collections의 Java 5 버전을 공개하는 대부분의 작업이 완료되었지만 아직 릴리스하지 않았습니다.
skaffman

나는 그것을 좋아한다. 그다지 유용하지 않다면 플러그라고 부릅니다.
geowa4

Jakarta가 왜 Apache Commons를 업데이트하지 않았는지 궁금합니다.
Uri

3
그들은 그것을 업데이트했습니다. 그것이 내가 말하는 것입니다. 그들은 단지 그것을 발표하지 않았습니다.
skaffman

23
Iterables.reverse는 더 이상 사용되지 않습니다. 대신 Lists.reverse 또는 ImmutableList.reverse를 사용하십시오.
개렛 홀

39

목록 (집합과 달리)은 순서가 지정된 모음이며이 목록을 반복하면 계약에 따라 주문이 유지됩니다. 스택이 역순으로 반복 될 것으로 예상했지만 불행히도 그렇지 않습니다. 그래서 내가 생각할 수있는 가장 간단한 해결책은 다음과 같습니다.

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

이것이 "각"루프 솔루션이 아니라는 것을 알고 있습니다. Google 컬렉션과 같은 새로운 라이브러리를 도입하는 것보다 for 루프를 사용하고 싶습니다.

Collections.reverse ()도 작업을 수행하지만 복사본을 역순으로 반환하는 대신 목록을 업데이트합니다.


3
이 방법은 배열 기반 목록 (예 : ArrayList)에는 적합 할 수 있지만 각 Get은 각 Get에 대해 목록을 처음부터 끝까지 (또는 끝까지) 순회해야하므로 Linked Lists에 대해서는 차선책입니다. Nat의 솔루션 에서처럼 더 스마트 한 반복자를 사용하는 것이 좋습니다 (List의 모든 구현에 최적).
Chris

1
더욱이, 그것은 for each구문 을 명시 적으로 요구하는 OP의 요청으로부터 벗어났다
Paul W

8

이것은 원래 목록을 망칠 것이고 루프 외부에서 호출해야합니다. 또한 루프 할 때마다 역전을 수행하고 싶지 않습니다 Iterables.reverse ideas.

Collections.reverse(stringList);

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

5

AFAIK 표준 라이브러리에는 이미 언어에 늦게 도입 된 구문 설탕 인 for-each 구문을 지원하는 표준 "reverse_iterator"종류가 없습니다.

for (Item 요소 : myList.clone (). reverse ())와 같은 작업을 수행하고 관련 가격을 지불 할 수 있습니다.

이것은 값 비싼 작업을 수행하는 편리한 방법을 제공하지 않는 명백한 현상과 상당히 일치하는 것으로 보입니다. 반복은 O (N ^ 2)가 될 수 있습니다. 물론 ArrayList가 있으면 그 가격을 지불하지 않습니다.


ListIterator를 거꾸로 실행할 수 있으며, Iterator 내에서 랩핑 될 수 있습니다.
Tom Hawtin-tackline

@Tom : 좋은 지적입니다. 그러나 반복자를 사용하면 여전히 성가신 구식 for 루프를 수행하고 있으며 마지막 요소부터 시작하는 데 드는 비용을 여전히 지불 할 수 있습니다 ...하지만 대답에 한정자를 추가했습니다. 감사합니다.
Uri

Deque에는 역 반복자가 있습니다.
Michael Munsey

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

2

현재 주석 : 당신은 아파치 코 몬즈를 사용할 수 있어야합니다ReverseListIterator

Iterable<String> reverse 
    = new IteratorIterable(new ReverseListIterator(stringList));

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

으로 @rogerdpack 말했다 , 당신은 포장 할 필요가 ReverseListIteratorint로서 Iterable.


1

사용자 지정 코드를 작성하지 않으면 요소를 뒤집을 열거자를 얻을 수 있습니다.

요소를 역순으로 리턴하는 Iterable의 사용자 정의 구현을 작성하여 Java에서이를 수행 할 수 있어야합니다.

그런 다음 랩퍼를 인스턴스화하거나 메소드를 호출하십시오. 그러면 루프마다 요소를 반전시키는 Iterable 구현이 리턴됩니다.



1

상자에서 각 구문에 대해를 사용하고 역순으로 진행하려면 컬렉션을 되돌려 야합니다.


1

위의 모든 답변은 다른 방법을 래핑하거나 외부 코드를 외부로 호출하여 요구 사항을 충족시킵니다.

다음은 Thinking in Java 4 판 11.11.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() - 1또는int current = super.size() - 1
qiwen li

1

주위에 작품 :

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

또는 구아바 와 함께 :

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

0

이 질문에 대한 답은 확실합니다. 한 가지 가능성은 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.