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

具体问题:

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

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

使用IntStream,我将得到这个编译器错误

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

我错过了什么?


当前回答

没有外部库…

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

}

其他回答

如何避免这样做:

不要使用.sorted(Comparator.reverseOrder())或.sorted(Collections.reverseOrder()),因为它只会按降序排序元素。 对于给定的Integer输入使用它: [1,4,2,5,3] 输出结果如下: [5,4,3,2,1] 对于字符串输入: [" a ", " d ", " b ", " e ", " c "] 输出结果如下: [e, d, c, b, a] 不要使用.sorted((a, b) -> -1)(后面的解释)

最简单的正确方法是:

List<Integer> list = Arrays.asList(1, 4, 2, 5, 3);
Collections.reverse(list);
System.out.println(list);

输出: [3,5,2,4,1]

String也一样:

List<String> stringList = Arrays.asList("A", "D", "B", "E", "C");
Collections.reverse(stringList);
System.out.println(stringList);

输出: [c, e, b, d, a]

不要使用.sorted((a, b) -> -1)! 它打破了比较国契约,可能只适用于某些情况。只在单线程上,而不是并行。 洋基的解释:

(a, b) -> -1打破了Comparator的契约。这是否有效取决于排序算法的实现。JVM的下一个版本可能会打破这一点。实际上,我已经可以在我的机器上使用IntStream重复地打破这个。.boxed范围(0 10000).parallel()()。sorted((a, b) -> -1).forEachOrdered(System.out::println);

//Don't use this!!!
List<Integer> list = Arrays.asList(1, 4, 2, 5, 3);
List<Integer> reversedList = list.stream()
        .sorted((a, b) -> -1)
        .collect(Collectors.toList());
System.out.println(reversedList);

正数输出: [3,5,2,4,1]

可能的输出在并行流或与其他JVM实现: [4,1,2,3,5]

String也一样:

//Don't use this!!!
List<String> stringList = Arrays.asList("A", "D", "B", "E", "C");
List<String> reversedStringList = stringList.stream()
        .sorted((a, b) -> -1)
        .collect(Collectors.toList());
System.out.println(reversedStringList);

正数输出: [c, e, b, d, a]

可能的输出在并行流或与其他JVM实现: [a, e, b, d, c]

作为参考,我正在考虑同样的问题,我想以相反的顺序连接流元素的字符串值。

itemList = {last, middle, first} =>第一个,中间,最后一个

我开始从comonad或Stuart Marks的ArrayDeque收集器中使用colltingandthen的中间收集,尽管我对中间收集不满意,然后再次使用流式

itemList.stream()
        .map(TheObject::toString)
        .collect(Collectors.collectingAndThen(Collectors.toList(),
                                              strings -> {
                                                      Collections.reverse(strings);
                                                      return strings;
                                              }))
        .stream()
        .collect(Collector.joining());

所以我迭代了Stuart Marks的答案,它使用了收集器。factory,它有一个有趣的结束项。

itemList.stream()
        .collect(Collector.of(StringBuilder::new,
                             (sb, o) -> sb.insert(0, o),
                             (r1, r2) -> { r1.insert(0, r2); return r1; },
                             StringBuilder::toString));

由于在这种情况下,流不是并行的,组合器不是那么相关,我使用插入无论如何为了代码的一致性,但这并不重要,因为它将取决于哪个stringbuilder是第一个构建的。

我看了看StringJoiner,但是它没有插入方法。

How about reversing the Collection backing the stream prior?

import java.util.Collections;
import java.util.List;

public void reverseTest(List<Integer> sampleCollection) {
    Collections.reverse(sampleCollection); // remember this reverses the elements in the list, so if you want the original input collection to remain untouched clone it first.

    sampleCollection.stream().forEach(item -> {
      // you op here
    });
}

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

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

像这样使用它:

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

原来的答案(包含一个错误-它不能正确工作的并行流):

一个通用的流反向方法可以是这样的:

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

一般问题:

流不存储任何元素。

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

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

更新:改变LinkedList为ArrayDeque(更好),详情请看这里

打印:

3

20

2

1

顺便说一下,使用sort方法是不正确的,因为它排序,而不是反转(假设流可能有无序元素)

具体问题:

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

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