Is there a sane way to get an ordered stream from a list (array list specifically, but it shouldn\'t matter) that streams elements in reverse of how they are in the original
Google's Guava library provides a reverse view of a list (Lists#reverse(List)). There's a ReverseListIterator in the Apache Commons Collection library, too.
If your List
is a random access list, you may simply use
int num=list.size()-1;
IntStream.rangeClosed(0, num).mapToObj(i->list.get(num-i))
to create a Stream
which has the characteristics ORDERED | SIZED | SUBSIZED
and offers full splitting support.
For a non-random access list like LinkedList
it would be a performance disaster, however, who uses LinkedList
anyway?
You may also check via list instanceof
RandomAccess first…
NOTE: If you have an ArrayList
or other list that allows random-access retrieval by index (get(i)
) then Holger's approach is preferable. The approach below is only necessary if you have a data structure that allows reverse traversal but not indexed access.
Unfortunately there doesn't seem to be a really simple (i.e., a one-liner) way to do this. But getting a reversed stream using AbstractSpliterator
isn't too difficult, given that List
already has the ability to iterate in reverse. Here's a utility method to do that:
static <T> Stream<T> reversedStream(List<? extends T> input) {
ListIterator<? extends T> li = input.listIterator(input.size());
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(input.size(), Spliterator.ORDERED) {
@Override public boolean tryAdvance(Consumer<? super T> action) {
if (li.hasPrevious()) {
action.accept(li.previous());
return true;
} else {
return false;
}
}
},
false);
}
(I suppose the Spliterator could be SIZED
, but that's mostly pointless because this is an unsplittable spliterator.)
As it stands, this can afford a limited degree of parallelism, as AbstractSpliterator
will call tryAdvance
multiple times and batch up work to hand off to fork-join tasks. But it's not as efficient as being able to split.
If parallel efficiency is a great concern, one could write a spliterator that can actually split, where the splits are traversed in reverse order.
I tend to like @teppic's answer of using a third-party library to do this. However, it's an interesting exercise to try to come up with a solution using only the Java 8 APIs. Delegating to a ListIterator
is the cleanest thing I could come up with, but it's not really much cleaner than implementing your own Iterator
from scratch.
public static void main(String[] args){
List<String> l = Arrays.asList("first", "second", "third");
StreamSupport.stream(Spliterators.spliterator(revit(l), l.size(), 0), false)
.forEachOrdered(System.out::println);
}
private static final <T> Iterator<T> revit(List<T> l){
ListIterator<T> li = l.listIterator(l.size());
return new Iterator<T>(){
@Override
public boolean hasNext(){
return li.hasPrevious();
}
@Override
public T next(){
return li.previous();
}
};
}