165

我需要使用 Java 以相反的顺序遍历列表。

所以它在哪里转发:

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

有没有办法使用for each语法以相反的顺序迭代 stringList?

为清楚起见:我知道如何以相反的顺序迭代列表,但想知道(出于好奇)如何在每种样式中执行此操作。

4

14 回答 14

157

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);
}
于 2009-07-08T13:41:31.437 回答
101

对于列表,您可以使用Google Guava Library

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

请注意,它不会反转整个集合,也不会做任何类似的事情——它只允许以相反的顺序进行迭代和随机访问。这比先反转集合更有效。Lists.reverse

要反转任意迭代,您必须全部阅读,然后向后“重放”它。

(如果你还没有使用它,我强烈建议你看看Guava。这是很棒的东西。)

于 2009-07-08T13:39:26.713 回答
43

List(与 Set 不同)是一个有序集合,对其进行迭代确实会按合同保留顺序。我本来希望 Stack 以相反的顺序迭代,但不幸的是它没有。所以我能想到的最简单的解决方案是:

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

我意识到这不是“每个”循环解决方案。我宁愿使用 for 循环,也不愿引入像 Google Collections 这样的新库。

Collections.reverse() 也可以完成这项工作,但它会更新列表,而不是以相反的顺序返回副本。

于 2011-05-20T16:01:53.113 回答
9

这将与原始列表混淆,并且还需要在循环之外调用。此外,您不想在每次循环时都执行反向操作 - 如果Iterables.reverse ideas应用其中一个,那会是真的吗?

Collections.reverse(stringList);

for(String string: stringList){
//...do something
}
于 2009-07-08T13:50:47.493 回答
5

AFAIK 标准库中没有标准的“reverse_iterator”之类的东西支持 for-each 语法,这已经是他们在语言后期引入的语法糖。

您可以执行类似 for(Item element: myList.clone().reverse()) 的操作并支付相关价格。

这似乎也与没有为您提供进行昂贵操作的便捷方法的明显现象相当一致 - 因为根据定义,列表可能具有 O(N) 随机访问复杂性(您可以使用单链接实现接口),反向迭代最终可能是 O(N^2)。当然,如果你有一个 ArrayList,你就不用付出这个代价。

于 2009-07-08T13:37:41.863 回答
3

截至评论:您应该能够使用 Apache CommonsReverseListIterator

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

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

正如@rogerdpack 所说,您需要ReverseListIteratorIterable.

正如Roland Nordborg-Løvstad 在评论中推荐的那样,您可以使用当前 Java 中的 Lambda 进行简化

Iterable<String> reverse = () -> new ReverseListIterator<>(stringList)
于 2016-01-26T16:21:24.893 回答
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);           
    }
}
于 2011-01-19T20:57:59.423 回答
2

周围的工作:

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

或与番石榴

Lists.reverse(stringList).forEach(str -> ...);
于 2019-09-16T09:48:48.597 回答
1

并非没有编写一些自定义代码,这些代码将为您提供一个枚举器,它将为您反转元素。

您应该能够在 Java 中通过创建 Iterable 的自定义实现来实现,它将以相反的顺序返回元素。

然后,您将实例化包装器(或调用方法,what-have-you),它将返回 Iterable 实现,该实现在 for each 循环中反转元素。

于 2009-07-08T13:37:57.197 回答
1

您可以使用Collections类来反转列表然后循环。

于 2009-07-08T13:38:38.927 回答
1

如果您想使用 for each 开箱即用的语法并以相反的顺序进行,则需要反转您的集合。

于 2009-07-08T13:39:13.297 回答
1

以上所有答案都只满足要求,要么包装另一种方法,要么在外面调用一些外来代码;

这是从Thinking in Java 第 4 版第 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
*///:~
于 2017-03-14T11:15:34.073 回答
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 中迭代列表的方法”

于 2018-09-01T22:08:15.377 回答
0

例如

Integer[][] a = {
                { 1, 2, 3 }, 
                { 4, 5, 6 }, 
                { 9, 8, 9 }, 
                };

List<List<Integer>> arr = Arrays.stream(a)
                .map(Arrays::asList)
                .collect(Collectors.toList());

现在扭转它。

Collections.reverse(arr);
System.out.println("Reverse Array" + arr);
for (List<Integer> obj : arr) {
    // TODO
}
于 2021-05-29T14:27:42.757 回答