java – Java 8 streams in reverse order

For the specific question to generate a IntStream reverse, try something like this:

static IntStream revRange(int from, int to) {
    return IntStream.range(from, to)
                    .map(i -> to - i + from - 1);
}

This avoids boxing and sorting.

For the general question of how to reverse a flow of any kind, I don’t know there is a “correct” way. There are a couple of ways I can think about. They both end up storing the elements of the flow. I don’t know of a way to reverse a flow without storing the elements.

This first way stores the elements in an array and reads them in a reverse order stream. Note that because we don’t know the runtime type of the stream elements, we can’t type the array correctly, requiring an unchecked cast.

@SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
    Object[] temp = input.toArray();
    return (Stream<T>) IntStream.range(0, temp.length)
                                .mapToObj(i -> temp[temp.length - i - 1]);
}

Another technique uses collectors to accumulate items in a reverse list. This makes a lot of insertions in the front of the objects ArrayListso there is a lot of copying going on.

Stream<T> input = ... ;
List<T> output =
    input.collect(ArrayList::new,
                  (list, e) -> list.add(0, e),
                  (list1, list2) -> list1.addAll(0, list2));

It is probably possible to write a much more efficient reversal manifold using some sort of custom data structure.

UPDATE 2016-01-29

Since this question has gotten some attention lately, I guess I should update my answer to fix the issue with inserting at the front of ArrayList. This will be horribly inefficient with a large number of elements, requiring the O (N ^ 2) copy.

It is preferable to use ArrayDeque, which efficiently supports foreground insertion. One small wrinkle is that we can’t use the three-arg form of Stream.collect(); requires that the contents of the second arg be merged with the first arg and that there is no “add-all-at-front” bulk operation on Deque. Instead, we use addAll() to add the contents of the first arg to the end of the second, and then return the second. This requires the use of the factory method Collector.of().

The complete code is this:

Deque<String> output =
    input.collect(Collector.of(
        ArrayDeque::new,
        (deq, t) -> deq.addFirst
        (d1, d2) -> { d2.addAll(d1); return d2; }));

The result is a Deque instead of a Listbut that shouldn’t be a big deal, as it can be easily iterated or streamed in the now reversed order.

Leave a comment