简体   繁体   中英

Can one do a for each loop in java in reverse order?

I need to run through a List in reverse order using Java.

So where this does it forwards:

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

Is there some way to iterate the stringList in reverse order using the for each syntax?

For clarity: I know how to iterate a list in reverse order but would like to know (for curiosity's sake ) how to do it in the for each style.

The Collections.reverse method actually returns a new list with the elements of the original list copied into it in reverse order, so this has O(n) performance with regards to the size of the original list.

As a more efficient solution, you could write a decorator that presents a reversed view of a List as an Iterable. The iterator returned by your decorator would use the ListIterator of the decorated list to walk over the elements in reverse order.

For example:

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

And you would use it like:

import static Reversed.reversed;

...

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

For a list, you could use the Google Guava Library :

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

Note that Lists.reverse doesn't reverse the whole collection, or do anything like it - it just allows iteration and random access, in the reverse order. This is more efficient than reversing the collection first.

To reverse an arbitrary iterable, you'd have to read it all and then "replay" it backwards.

(If you're not already using it, I'd thoroughly recommend you have a look at the Guava . It's great stuff.)

The List (unlike the Set) is an ordered collection and iterating over it does preserve the order by contract. I would have expected a Stack to iterate in the reverse order but unfortunately it doesn't. So the simplest solution I can think of is this:

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

I realize that this is not a "for each" loop solution. I'd rather use the for loop than introducing a new library like the Google Collections.

Collections.reverse() also does the job but it updates the list as opposed to returning a copy in reverse order.

This will mess with the original list and also needs to be called outside of the loop. Also you don't want to perform a reverse every time you loop - would that be true if one of the Iterables.reverse ideas was applied?

Collections.reverse(stringList);

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

AFAIK there isn't a standard "reverse_iterator" sort of thing in the standard library that supports the for-each syntax which is already a syntactic sugar they brought late into the language.

You could do something like for(Item element: myList.clone().reverse()) and pay the associated price.

This also seems fairly consistent with the apparent phenomenon of not giving you convenient ways to do expensive operations - since a list, by definition, could have O(N) random access complexity (you could implement the interface with a single-link), reverse iteration could end up being O(N^2). Of course, if you have an ArrayList, you don't pay that price.

As of the comment : You should be able to use Apache Commons ReverseListIterator

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

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

As @rogerdpack said , you need to wrap the ReverseListIterator as an Iterable .

As recommended by Roland Nordborg-Løvstad in the comments , you can simplify with Lambdas in current Java

Iterable<String> reverse = () -> new ReverseListIterator<>(stringList)

This may be an option. Hope there is a better way to start from last element than to while loop to the end.

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

Not without writing some custom code which will give you an enumerator which will reverse the elements for you.

You should be able to do it in Java by creating a custom implementation of Iterable which will return the elements in reverse order.

Then, you would instantiate the wrapper (or call the method, what-have-you) which would return the Iterable implementation which reverses the element in the for each loop.

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

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

All answers above only fulfill the requirement, either by wrapping another method or calling some foreign code outside;

Here is the solution copied from the Thinking in Java 4th edition , chapter 11.13.1 AdapterMethodIdiom ;

Here is the code:

// 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
*///:~

A work Around :

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

Or with guava :

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

Definitely a late answer to this question. One possibility is to use the ListIterator in a for loop. It's not as clean as colon-syntax, but it works.

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

Credit for the ListIterator syntax goes to "Ways to iterate over a list in Java"

Eg

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

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

Reverse it now.

Collections.reverse(arr);
System.out.println("Reverse Array" + arr);
for (List<Integer> obj : arr) {
    // TODO
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM