Java 8 流逆序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24010109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 09:42:02  来源:igfitidea点击:

Java 8 stream reverse order

javalistsortingjava-8java-stream

提问by vach

General question: What's the proper way to reverse a stream? Assuming that we don't know what type of elements that stream consists of, what's the generic way to reverse any stream?

一般问题:反转流的正确方法是什么?假设我们不知道流由什么类型的元素组成,那么反转任何流的通用方法是什么?

Specific question:

具体问题:

IntStreamprovides range method to generate Integers in specific range IntStream.range(-range, 0), now that I want to reverse it switching range from 0 to negative won't work, also I can't use Integer::compare

IntStream提供范围方法来生成特定范围内的整数IntStream.range(-range, 0),现在我想反转它,从 0 到负的切换范围不起作用,我也不能使用Integer::compare

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);

with IntStreamI'll get this compiler error

IntStream我会得到这个编译器错误

Error:(191, 0) ajc: The method sorted()in the type IntStreamis not applicable for the arguments (Integer::compare)

错误:(191, 0) ajc:sorted()类型中的方法IntStream不适用于参数 ( Integer::compare)

what am I missing here?

我在这里错过了什么?

采纳答案by Stuart Marks

For the specific question of generating a reverse IntStream, try something like this:

对于生成 reverse 的具体问题IntStream,请尝试以下操作:

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 stream of any type, I don't know of there's a "proper" way. There are a couple ways I can think of. Both end up storing the stream elements. I don't know of a way to reverse a stream without storing the elements.

对于如何反转任何类型的流的一般问题,我不知道有一种“正确”的方法。我可以想到几种方法。两者最终都存储了流元素。我不知道有什么方法可以在不存储元素的情况下反转流。

This first way stores the elements into an array and reads them out to a stream in reverse order. Note that since we don't know the runtime type of the stream elements, we can't type the array properly, 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 the items into a reversed list. This does lots of insertions at the front of ArrayListobjects, so there's lots of copying going on.

另一种技术使用收集器将项目累积到一个反向列表中。这会在ArrayList对象的前面进行大量插入,因此会进行大量复制。

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

It's probably possible to write a much more efficient reversing collector using some kind of customized data structure.

使用某种自定义数据结构编写更高效的反向收集器可能是可能的。

UPDATE 2016-01-29

更新 2016-01-29

Since this question has gotten a bit of attention recently, I figure I should update my answer to solve the problem with inserting at the front of ArrayList. This will be horribly inefficient with a large number of elements, requiring O(N^2) copying.

由于这个问题最近引起了一些关注,我想我应该更新我的答案以解决在ArrayList. 对于大量元素,这将是非常低效的,需要 O(N^2) 复制。

It's preferable to use an ArrayDequeinstead, which efficiently supports insertion at the front. A small wrinkle is that we can't use the three-arg form of Stream.collect(); it requires the contents of the second arg be merged into the first arg, and there's no "add-all-at-front" bulk operation on Deque. Instead, we use addAll()to append the contents of the first arg to the end of the second, and then we return the second. This requires using the Collector.of()factory method.

最好使用 anArrayDeque代替,它有效地支持在前面插入。一个小问题是我们不能使用 ; 的三参数形式Stream.collect()。它需要将第二个 arg 的内容合并到第一个 arg 中,并且在Deque. 相反,我们使用addAll()将第一个 arg 的内容附加到第二个的末尾,然后返回第二个。这需要使用Collector.of()工厂方法。

The complete code is this:

完整的代码是这样的:

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

The result is a Dequeinstead of a List, but that shouldn't be much of an issue, as it can easily be iterated or streamed in the now-reversed order.

结果是 aDeque而不是 a List,但这应该不是什么大问题,因为它可以很容易地以现在颠倒的顺序进行迭代或流式传输。

回答by vach

Here's the solution I've come up with:

这是我想出的解决方案:

private static final Comparator<Integer> BY_ASCENDING_ORDER = Integer::compare;
private static final Comparator<Integer> BY_DESCENDING_ORDER = BY_ASCENDING_ORDER.reversed();

then using those comparators:

然后使用这些比较器:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...

回答by Venkata Raju

General Question:

一般问题:

Stream does not store any elements.

Stream 不存储任何元素。

So iterating elements in the reverse order is not possible without storing the elements in some intermediate collection.

因此,如果不将元素存储在某个中间集合中,就不可能以相反的顺序迭代元素。

Stream.of("1", "2", "20", "3")
      .collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList
      .descendingIterator()
      .forEachRemaining(System.out::println);

Update: Changed LinkedList to ArrayDeque (better) see here for details

更新:将 LinkedList 更改为 ArrayDeque(更好),请参阅此处了解详细信息

Prints:

印刷:

3

20

2

1

By the way, using sortmethod is not correct as it sorts, NOT reverses (assuming stream may have unordered elements)

顺便说一句, usingsort方法在排序时不正确,而不是反向(假设流可能具有无序元素)

Specific Question:

具体问题:

I found this simple, easier and intuitive(Copied @Holger comment)

我发现这很简单,更容易和直观(复制@Holger评论

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)

回答by lexicalscope

You could define your own collector that collects the elements in reverse order:

您可以定义自己的收集器,以相反的顺序收集元素:

public static <T> Collector<T, List<T>, List<T>> inReverse() {
    return Collector.of(
        ArrayList::new,
        (l, t) -> l.add(t),
        (l, r) -> {l.addAll(r); return l;},
        Lists::<T>reverse);
}

And use it like:

并像这样使用它:

stream.collect(inReverse()).forEach(t -> ...)

I use an ArrayList in forward order to efficiently insert collect the items (at the end of the list), and Guava Lists.reverse to efficiently give a reversed view of the list without making another copy of it.

我使用 ArrayList 以正向顺序有效地插入收集项目(在列表的末尾),并使用 Guava Lists.reverse 有效地提供列表的反向视图,而无需制作另一个副本。

Here are some test cases for the custom collector:

以下是自定义收集器的一些测试用例:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

import org.hamcrest.Matchers;
import org.junit.Test;

import com.google.common.collect.Lists;

public class TestReverseCollector {
    private final Object t1 = new Object();
    private final Object t2 = new Object();
    private final Object t3 = new Object();
    private final Object t4 = new Object();

    private final Collector<Object, List<Object>, List<Object>> inReverse = inReverse();
    private final Supplier<List<Object>> supplier = inReverse.supplier();
    private final BiConsumer<List<Object>, Object> accumulator = inReverse.accumulator();
    private final Function<List<Object>, List<Object>> finisher = inReverse.finisher();
    private final BinaryOperator<List<Object>> combiner = inReverse.combiner();

    @Test public void associative() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r1, Matchers.equalTo(r2));
    }

    @Test public void identity() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, supplier.get()));

        assertThat(r1, equalTo(r2));
    }

    @Test public void reversing() throws Exception {
        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);

        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t3);
        accumulator.accept(a3, t4);

        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r2, contains(t4, t3, t2, t1));
    }

    public static <T> Collector<T, List<T>, List<T>> inReverse() {
        return Collector.of(
            ArrayList::new,
            (l, t) -> l.add(t),
            (l, r) -> {l.addAll(r); return l;},
            Lists::<T>reverse);
    }
}

回答by comonad

without external lib...

没有外部库...

import java.util.List;
import java.util.Collections;
import java.util.stream.Collector;

public class MyCollectors {

    public static <T> Collector<T, ?, List<T>> toListReversed() {
        return Collectors.collectingAndThen(Collectors.toList(), l -> {
            Collections.reverse(l);
            return l;
        });
    }

}

回答by John McClean

cyclops-reactStreamUtils has a reverse Stream method (javadoc).

cyclops -reactStreamUtils 有一个反向流方法(javadoc)。

  StreamUtils.reverse(Stream.of("1", "2", "20", "3"))
             .forEach(System.out::println);

It works by collecting to an ArrayList and then making use of the ListIterator class which can iterate in either direction, to iterate backwards over the list.

它的工作原理是收集到一个 ArrayList,然后利用可以在任一方向迭代的 ListIterator 类来向后迭代列表。

If you already have a List, it will be more efficient

如果你已经有一个List,效率会更高

  StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
             .forEach(System.out::println);

回答by SlavaSt

One could write a collector that collects elements in reversed order:

可以编写一个以相反顺序收集元素的收集器:

public static <T> Collector<T, ?, Stream<T>> reversed() {
    return Collectors.collectingAndThen(Collectors.toList(), list -> {
        Collections.reverse(list);
        return list.stream();
    });
}

And use it like this:

并像这样使用它:

Stream.of(1, 2, 3, 4, 5).collect(reversed()).forEach(System.out::println);

Original answer(contains a bug - it does not work correctly for parallel streams):

原始答案(包含一个错误 - 它对并行流无法正常工作):

A general purpose stream reverse method could look like:

通用流反向方法可能如下所示:

public static <T> Stream<T> reverse(Stream<T> stream) {
    LinkedList<T> stack = new LinkedList<>();
    stream.forEach(stack::push);
    return stack.stream();
}

回答by parmeshwor11

The most generic and the easiest way to reverse a list will be :

反转列表的最通用和最简单的方法是:

public static <T> void reverseHelper(List<T> li){

 li.stream()
.sorted((x,y)-> -1)
.collect(Collectors.toList())
.forEach(System.out::println);

    }

回答by Brandon

Many of the solutions here sort or reverse the IntStream, but that unnecessarily requires intermediate storage. Stuart Marks's solutionis the way to go:

这里的许多解决方案对 进行排序或反转IntStream,但这不必要地需要中间存储。Stuart Marks 的解决方案是要走的路:

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

It correctly handles overflow as well, passing this test:

它也正确处理溢出,通过了这个测试:

@Test
public void testRevRange() {
    assertArrayEquals(revRange(0, 5).toArray(), new int[]{4, 3, 2, 1, 0});
    assertArrayEquals(revRange(-5, 0).toArray(), new int[]{-1, -2, -3, -4, -5});
    assertArrayEquals(revRange(1, 4).toArray(), new int[]{3, 2, 1});
    assertArrayEquals(revRange(0, 0).toArray(), new int[0]);
    assertArrayEquals(revRange(0, -1).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MAX_VALUE, MAX_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE + 1).toArray(), new int[]{MIN_VALUE});
    assertArrayEquals(revRange(MAX_VALUE - 1, MAX_VALUE).toArray(), new int[]{MAX_VALUE - 1});
}

回答by Antonio

Java 8 way to do this:

Java 8 的方式来做到这一点:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);