在java中以倒序遍历列表

我正在迁移一段代码以使用泛型。这样做的一个理由是,for循环比跟踪索引或使用显式迭代器干净得多。

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

有人能建议一种更干净的方式来这样做(因为我不喜欢indexed for loop时与集合工作),虽然它确实工作?

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

我不能在JDK之外添加任何新的依赖项。

259793 次浏览

创建一个自定义reverseIterable

选项1:你有没有想过用收藏#反向()反转List,然后使用foreach?

当然,您可能还想重构代码,使列表的顺序正确,这样就不必反转它,这会占用额外的空间/时间。


编辑:

选项2:或者,你可以使用双端队列代替数组列表吗?它将允许您向前和向后迭代


编辑:

选项3:正如其他人建议的那样,你可以编写一个Iterator,它将反向遍历列表,下面是一个例子:

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

我认为使用for循环语法是不可能的。我唯一的建议就是:

Collections.reverse(list);
for (Object o : list) {
...
}

... 但我不会说这个“更干净”,因为它的效率会更低。

试试这个:

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

下面是ReverseIterable的一个(未经测试的)实现。当iterator()被调用时,它创建并返回一个私有的ReverseIterator实现,它简单地将对hasNext()的调用映射到hasPrevious(),而对next()的调用则映射到previous()。这意味着你可以逆向遍历ArrayList,如下所示:

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

类定义

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

还找到了谷歌集合反向方法。

你可以使用具体类LinkedList来代替通用接口List。然后你有一个descendingIterator用于反向迭代。

LinkedList<String > linkedList;
for( Iterator<String > it = linkedList.descendingIterator(); it.hasNext(); ) {
String text = it.next();
}

不知道为什么没有descendingIteratorArrayList

例子很简单:

List<String> list = new ArrayList<String>();


list.add("ravi");


list.add("kant");


list.add("soni");


// Iterate to disply : result will be as ---     ravi kant soni


for (String name : list) {
...
}


//Now call this method


Collections.reverse(list);


// iterate and print index wise : result will be as ---     soni kant ravi


for (String name : list) {
...
}

原因:“不知道为什么没有ArrayList的descent iterator…”

因为数组list不保持列表在相同的顺序数据已添加到列表。所以,千万不要用数组列表。

链表将保持数据与ADD到list的顺序相同。

所以,在上面的例子中,我使用了ArrayList()来让用户扭曲他们的思想,让他们从他们的角度去锻炼一些东西。

而不是这个

List<String> list = new ArrayList<String>();

使用:

List<String> list = new LinkedList<String>();


list.add("ravi");


list.add("kant");


list.add("soni");


// Iterate to disply : result will be as ---     ravi kant soni


for (String name : list) {
...
}


//Now call this method


Collections.reverse(list);


// iterate and print index wise : result will be as ---     soni kant ravi


for (String name : list) {
...
}

代码如下所示:

List<Item> items;
...
for (Item item : In.reverse(items))
{
...
}

把这段代码放到一个名为“In.java”的文件中:

import java.util.*;


public enum In {;
public static final <T> Iterable<T> reverse(final List<T> list) {
return new ListReverseIterable<T>(list);
}


class ListReverseIterable<T> implements Iterable<T> {
private final List<T> mList;


public ListReverseIterable(final List<T> list) {
mList = list;
}


public Iterator<T> iterator() {
return new Iterator<T>() {
final ListIterator<T> it = mList.listIterator(mList.size());


public boolean hasNext() {
return it.hasPrevious();
}
public T next() {
return it.previous();
}
public void remove() {
it.remove();
}
};
}
}
}

番石榴提供Lists#reverse(List)ImmutableList#reverse()。在Guava的大多数情况下,如果参数是ImmutableList,则前者将委托给后者,因此可以在所有情况下使用前者。它们不会创建列表的新副本,而只是列表的“反向视图”。

例子

List reversed = ImmutableList.copyOf(myList).reverse();

正如至少两次建议的那样,你可以将descendingIteratorDeque一起使用,特别是与LinkedList一起使用。如果你想使用for-each循环(即有Iterable),你可以像这样构造和使用包装器:

import java.util.*;


public class Main {


public static class ReverseIterating<T> implements Iterable<T> {
private final LinkedList<T> list;


public ReverseIterating(LinkedList<T> list) {
this.list = list;
}


@Override
public Iterator<T> iterator() {
return list.descendingIterator();
}
}


public static void main(String... args) {
LinkedList<String> list = new LinkedList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");


for (String s : new ReverseIterating<String>(list)) {
System.out.println(s);
}
}
}

如果列表相当小,所以性能不是真正的问题,可以使用Google Guava中__abc1 -类的__abc0 -方法。生成漂亮的__abc3代码,并且原始列表保持不变。另外,反向列表是由原始列表支持的,因此对原始列表的任何更改都将反映在反向列表中。

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

产生如下结果:

[one, two, three]
[three, two, one]
[one, two, three, four]
[four, three, two, one]

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

for (final String someString : Lists.reverse(myList)) {
//do something
}

这是一个老问题,但它缺乏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
Valid for Java 9+


List<String> strList = List.of("a", "b", "c", "d", "e");


IntStream.iterate(strList.size() - 1, i -> i >= 0, i -> --i)
.mapToObj(strList::get)
.forEach(System.out::println);

如何使用DeQue:

  var queue = new ArrayDeque<>(list);
while (!queue.isEmpty()) {
var first = reversed ? queue.removeLast() : queue.removeFirst();
var second = reversed ? queue.peekLast() : queue.peekFirst();
if (second != null) {
//your code goes here
}
}