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

16

173

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.

Madiemadigan answered 8/7, 2009 at 13:34 Comment(5)
The point of the "for-each" loop is that you just need to perform an operation on each element, and order is not important. For-each could process the elements in completely random order, and it would still be doing what it was designed for. If you need to process the elements in a particular way, I would suggest doing it manually.Validate
Java collections library. Not really anything to do with the language. Blame Josh Bloch.Keratoplasty
@muusbolla: But as a List is an ordered collection, surely its order will be honoured, regardless? Therefore for-each will not process the elements of a List in a random order.Bevins
@Validate that is not true. Maybe in the case of Set derived collections. foreach guarantees iteration in the order of the iterator returned from the iterator() method of the collection. docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.htmlProthalamium
I want this build-in too, in Python we could write this for row in rows[::-1] to do reversing iteration. Why not in Java?Female
K
160

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);
}
Koziol answered 8/7, 2009 at 13:41 Comment(4)
That's basically what Google's Iterables.reverse does, yes :)Swaziland
I know there is a 'rule' that we have to accept Jon's answer :) but.. I want to accept this one (even though they are essentially the same) because it does not require me to include another 3rd party library (even though some could argue that that reason breaks one of the prime advantages of OO - reusability).Madiemadigan
Small error: In public void remove(), there should not be a return statement, it should be just: i.remove();Ambivert
Collections.reverse() does NOT return a reversed copy but acts on the List passed to it as a parameter. Like your solution with the iterator, though. Really elegant.Charcot
S
101

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

Swaziland answered 8/7, 2009 at 13:39 Comment(11)
Our codebase makes heavy use of the generified version of Commons Collections released by larvalabs (larvalabs.com/collections). Looking through the SVN repo for Apache Commons, it's clear that most of the work in releasing a java 5 version of Commons Collections is done, they just haven't released it yet.Overrun
I like it. If it weren't so useful, I'd call that a plug.Immoderation
I wonder why Jakarta never bothered to update Apache Commons.Hix
They have updated it, that's what I'm saying. They just haven't released it.Overrun
Iterables.reverse has been deprecated, use Lists.reverse or ImmutableList.reverse instead.Occult
it seems Guava (19.0) does not have any implementation of Reverse anymore. is there any other alternative for the Lists.Reverse?Lombok
@AaA: What makes you say that? github.com/google/guava/blob/master/guava/src/com/google/common/…Swaziland
I downloaded guava from here and my code give me cannot find symbol with arrow under reverse. here is my code: private final List<Class2> MyClasses; public void aMethod() { for (Class2 MC1: com.google.common.collect.Lists.reverse(MyClasses)) { MC1.method(); } }Lombok
@AaA: Well I've just downloaded the jar file and tried it myself, and it's fine for me. I can't tell what you're doing wrong - I suggest you ask a new question with a minimal reproducible example.Swaziland
Someone from my team included the google-collections-1.0 in project which has a different definition of com.google.common.collect.Lists. removing that library fixed the issue.Lombok
Further, you can use Guava's Lists.reverse to iterate arrays by combining with Arrays.asList (a non-copying List wrapper over the array) or, for primitive types, the various Guava asList methods such as e.g. Ints.asList (although in the latter case you'll end up wastefully boxing and unboxing each primitive).Seer
A
46

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.

Approval answered 20/5, 2011 at 16:1 Comment(3)
This approach may be fine for array based lists (such as ArrayList) but it would be sub-optimal for Linked Lists as each get would have to traverse the list from beginning to end (or possibly end to beginning) for each get. It is better to use a smarter iterator as in Nat's solution (optimal for all implementations of List).Inebriate
Further, it strays from the request in the OP which explicitly asks for the for each syntaxCaron
better readability: for (int i = stack.size(); i-- >0;) {Marge
M
11

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
}
Morell answered 8/7, 2009 at 13:50 Comment(0)
H
6

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.

Hix answered 8/7, 2009 at 13:37 Comment(3)
You can run a ListIterator backwards, which can be wrapped up within an Iterator.Keratoplasty
@Tom: Good point. However, with the iterator you are still doing the annoying old-style for loops, and you may still pay the cost to get to the last element to begin with... I added the qualifier in my answer, though, thanks.Hix
Deque has a reverse iterator.Valina
M
3

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)
Mathildamathilde answered 26/1, 2016 at 16:21 Comment(1)
In later java versions, you can use this shorthand: Iterable<String> reverse = () -> new ReverseListIterator<>(stringList)Assyriology
T
2

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);           
    }
}
Tatianna answered 19/1, 2011 at 20:57 Comment(1)
The better way is a.listIterator(a.size()).Tearoom
S
2

A work Around :

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

Or with guava :

Lists.reverse(stringList).forEach(str -> ...);
Sharla answered 16/9, 2019 at 9:48 Comment(0)
M
2

Starting with Java 21, the reversed() method can be used to return a reversed view on the list, which can then be iterated over using an enhanced for statement:

for (String string : stringList.reversed()) {
    //...do something
}
Multiflorous answered 19/7, 2023 at 5:24 Comment(0)
W
1

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.

Waylay answered 8/7, 2009 at 13:37 Comment(0)
S
1

You can use the Collections class to reverse the list then loop.

Schreiner answered 8/7, 2009 at 13:38 Comment(0)
F
1

You'd need to reverse your collection if you want to use the for each syntax out of the box and go in reverse order.

Forgetmenot answered 8/7, 2009 at 13:39 Comment(0)
L
1

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
*///:~
Luminous answered 14/3, 2017 at 11:15 Comment(1)
why is the int current = size() - 1 right? why not the int current = this.size() - 1 or int current = super.size() - 1Luminous
C
0

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"

Cowcatcher answered 1/9, 2018 at 22:8 Comment(0)
G
0

E.g.

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
}
Geesey answered 29/5, 2021 at 14:27 Comment(0)
F
0

You can iterate through an array in reverse order using For-Each loop like below:

public class Main {
    public static void main(String[] args) {
        int arr[] = {2,3,1,4,7,6};
        int i = arr.length-1;
        for(int e: arr){
            // do something
            System.out.print(arr[i] + "\t");
            i--;
        }
    }
}
Finstad answered 10/1 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.