在java中以相反的顺序遍历列表

All*_*nde 241 java collections

我正在迁移一段代码以利用泛型.这样做的一个论点是for循环比跟踪索引或使用显式迭代器更清晰.

在大约一半的情况下,列表(ArrayList)通过今天使用索引以相反的顺序迭代.

有人可以建议一种更清洁的方式(因为我不喜欢indexed for loop使用集合时),虽然它确实有用吗?

 for (int i = nodes.size() - 1; i >= 0; i--) {
    final Node each = (Node) nodes.get(i);
    ...
 }
Run Code Online (Sandbox Code Playgroud)

注意:我无法在JDK之外添加任何新的依赖项.

Joh*_*lla 435

试试这个:

// Substitute appropriate type.
ArrayList<...> a = new ArrayList<...>();

// Add elements to list.

// Generate an iterator. Start just after the last element.
ListIterator li = a.listIterator(a.size());

// Iterate in reverse.
while(li.hasPrevious()) {
  System.out.println(li.previous());
}
Run Code Online (Sandbox Code Playgroud)

  • 在没有索引参数的情况下调用listIterator()将在列表的开头给出一个Iterator,因此hasPrevious()将在第一次调用时返回false. (26认同)
  • 不错.不使用索引,但是失去了每种语法的优雅.+1无论如何. (4认同)
  • 我想你想要`listIterator`调用的索引. (2认同)

Geo*_*eng 34

番石榴提供Lists#reverse(List)ImmutableList#reverse().对于Guava的大多数情况,如果参数是a ImmutableList,前者会委托给后者,所以你可以在所有情况下使用前者.这些不会创建列表的新副本,而只是创建它的"反转视图".

List reversed = ImmutableList.copyOf(myList).reverse();
Run Code Online (Sandbox Code Playgroud)


Ada*_*ski 24

我不认为使用for循环语法是可能的.我唯一能建议的是做一些事情:

Collections.reverse(list);
for (Object o : list) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

......但我不会说这是"更清洁",因为效率会降低.

  • 它也会改变您遍历的列表,这是一个非常大的副作用.(假设你将它包装在一个方法中,每次调用时你都以另一种方式遍历列表^^) (24认同)

Kev*_*vin 15

选项1:您是否考虑过使用集合反转列表#verse()然后使用foreach?

当然,您可能还需要重构代码,以便正确排序列表,这样您就不必反转它,这会占用额外的空间/时间.


编辑:

选项2:或者,您可以使用Deque而不是ArrayList吗?它将允许您向前和向后迭代


编辑:

选项3:正如其他人所建议的那样,你可以编写一个迭代通过列表的迭代器,这是一个例子:

import java.util.Iterator;
import java.util.List;

public class ReverseIterator<T> implements Iterator<T>, Iterable<T> {

    private final List<T> list;
    private int position;

    public ReverseIterator(List<T> list) {
        this.list = list;
        this.position = list.size() - 1;
    }

    @Override
    public Iterator<T> iterator() {
        return this;
    }

    @Override
    public boolean hasNext() {
        return position >= 0;
    }

    @Override
    public T next() {
        return list.get(position--);
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }

}


List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");

for (String s : new ReverseIterator<String>(list)) {
    System.out.println(s);
}
Run Code Online (Sandbox Code Playgroud)

  • +对于Deque; 典型的实现有`descendingIterator()`. (3认同)

tan*_*ens 11

您可以使用具体类LinkedList而不是通用接口List.然后你有一个descendingIterator反向迭代.

LinkedList<String > linkedList;
for( Iterator<String > it = linkedList.descendingIterator(); it.hasNext(); ) {
    String text = it.next();
}
Run Code Online (Sandbox Code Playgroud)

不知道为什么没有descendingIteratorArrayList


Fed*_*ner 8

这是一个老问题,但它缺乏java8友好的答案.以下是在Streaming API的帮助下反向迭代列表的一些方法:

List<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 3, 3, 7, 5));
list.stream().forEach(System.out::println); // 1 3 3 7 5

int size = list.size();

ListIterator<Integer> it = list.listIterator(size);
Stream.generate(it::previous).limit(size)
    .forEach(System.out::println); // 5 7 3 3 1

ListIterator<Integer> it2 = list.listIterator(size);
Stream.iterate(it2.previous(), i -> it2.previous()).limit(size)
    .forEach(System.out::println); // 5 7 3 3 1

// If list is RandomAccess (i.e. an ArrayList)
IntStream.range(0, size).map(i -> size - i - 1).map(list::get)
    .forEach(System.out::println); // 5 7 3 3 1

// If list is RandomAccess (i.e. an ArrayList), less efficient due to sorting
IntStream.range(0, size).boxed().sorted(Comparator.reverseOrder())
    .map(list::get).forEach(System.out::println); // 5 7 3 3 1
Run Code Online (Sandbox Code Playgroud)

  • 非常好,只是一个整容改变:int size = list.size(); ListIterator <Integer> it = list.listIterator(size); Stream.generate(它以前::).limit(大小).forEach(的System.out ::的println); (2认同)

M. *_*tin 6

Java 21 添加了一个reversed()方法List,它返回列表的反向视图。这可用于以相反顺序迭代。

for (Node each : nodes.reversed()) {
   ...
}
Run Code Online (Sandbox Code Playgroud)


nan*_*nda 5

创建自定义reverseIterable.

  • 投票,因为我不认为这是一个完整的答案. (17认同)

Ada*_*ski 5

这是ReverseIterable. 当iterator()被调用时,它会创建并返回一个私有ReverseIterator实现,它只是将调用映射到hasNext()tohasPrevious()和调用next()映射到previous()。这意味着您可以ArrayList按如下方式反向迭代:

ArrayList<String> l = ...
for (String s : new ReverseIterable(l)) {
  System.err.println(s);
}
Run Code Online (Sandbox Code Playgroud)

类定义

public class ReverseIterable<T> implements Iterable<T> {
  private static class ReverseIterator<T> implements Iterator {
    private final ListIterator<T> it;

    public boolean hasNext() {
      return it.hasPrevious();
    }

    public T next() {
      return it.previous();
    }

    public void remove() {
      it.remove();
    }
  }

  private final ArrayList<T> l;

  public ReverseIterable(ArrayList<T> l) {
    this.l = l;
  }

  public Iterator<T> iterator() {
    return new ReverseIterator(l.listIterator(l.size()));
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的实现,但是 `ReverseIterator` 缺少必要的构造函数,代码应该使用 `List` 而不是 `ArrayList`。 (2认同)

Tob*_*obb 5

如果列表相当小以致性能不是真正的问题,那么可以使用-class 的reverse-metod .产生漂亮的代码,原始列表保持不变.此外,反转列表由原始列表支持,因此对原始列表的任何更改都将反映在反向列表中.ListsGoogle Guavafor-each

import com.google.common.collect.Lists;

[...]

final List<String> myList = Lists.newArrayList("one", "two", "three");
final List<String> myReverseList = Lists.reverse(myList);

System.out.println(myList);
System.out.println(myReverseList);

myList.add("four");

System.out.println(myList);
System.out.println(myReverseList);
Run Code Online (Sandbox Code Playgroud)

得到以下结果:

[one, two, three]
[three, two, one]
[one, two, three, four]
[four, three, two, one]
Run Code Online (Sandbox Code Playgroud)

这意味着myList的反向迭代可以写成:

for (final String someString : Lists.reverse(myList)) {
    //do something
}
Run Code Online (Sandbox Code Playgroud)