【问题标题】:Java 8 stream reverse orderJava 8 流逆序
【发布时间】:2014-07-23 11:46:04
【问题描述】:

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

具体问题:

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

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

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

我在这里错过了什么?

【问题讨论】:

  • 一个IntStream 没有.sorted(Comparator) 方法;您必须先通过 Stream&lt;Integer&gt; 并在此处反转,然后才能产生 IntStream
  • 好的,我找到了,你需要先用.boxed()再用.sorted()
  • 要以相反的顺序生成IntStream.range(0, n),请执行map(i -&gt; n - i - 1) 之类的操作。无需装箱和排序。
  • 您的一般问题和您的具体问题对我来说就像两个完全不同的问题。一般说的是倒转stream,而具体说的是按降序排列数字。如果流以像1, 3, 2 这样的无序方式生成数字,您的预期结果是什么?你想要像2, 3, 1这样的反向流还是像3, 2, 1这样的排序流?
  • 一般情况下,您不能反转流 - 例如,流可能是无限的。

标签: java list sorting java-8 java-stream


【解决方案1】:

对于生成反向IntStream的具体问题,试试这样的:

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

这样可以避免装箱和排序。

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

第一种方法将元素存储到一个数组中,然后以相反的顺序将它们读出到一个流中。请注意,由于我们不知道流元素的运行时类型,因此我们无法正确键入数组,需要未经检查的强制转换。

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

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

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

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

2016 年 1 月 29 日更新

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

最好改用ArrayDeque,它可以有效地支持在前面插入。一个小问题是我们不能使用Stream.collect() 的三参数形式;它要求将第二个 arg 的内容合并到第一个 arg 中,并且在 Deque 上没有“add-all-at-front”批量操作。相反,我们使用addAll() 将第一个 arg 的内容附加到第二个的末尾,然后返回第二个。这需要使用Collector.of() 工厂方法。

完整的代码是这样的:

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

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

【讨论】:

  • 或者:IntStream.iterate(to-1, i-&gt;i-1).limit(to-from)
  • @Holger 不幸的是,该解决方案无法正确处理溢出。
  • @Brandon Mintern:确实,您必须改用.limit(endExcl-(long)startIncl),但是对于如此大的流,无论如何都非常不鼓励使用它,因为它比基于range 的解决方案效率低得多。在我写评论时,我并没有意识到效率差异。
【解决方案2】:

优雅的解决方案

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream()
    .boxed() // Converts Intstream to Stream<Integer>
    .sorted(Collections.reverseOrder()) // Method on Stream<Integer>
    .forEach(System.out::println);

【讨论】:

  • 它很优雅但并不完全有效,因为列表的元素必须是Comparable...
  • 假设我们希望元素以相反的顺序排序。问题是关于反转流的顺序。
  • @KrzysztofWolny 您可以将比较器函数传递给 reverseOrder(),这样就不会太麻烦了
【解决方案3】:

一般问题:

Stream 不存储任何元素。

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

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

更新:将 LinkedList 更改为 ArrayDeque(更好)see here for details

打印:

3

20

2

1

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

具体问题:

我发现这个简单、容易和直观(复制@Holger comment

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

【讨论】:

  • 一些流操作,例如sorteddistinct实际上存储了一个中间结果。有关这方面的一些信息,请参阅package API docs
  • @Lii 我在同一页看到No storage。即使它存储我们也无法访问该存储(所以我猜No storage 很好)
  • 好答案。但是由于使用了额外的空间,程序员在非常大的集合上使用你的方法并不是一个好主意。
  • 我喜欢从可理解性的角度来看解决方案的简单性,并在现有数据结构上利用现有方法......以前使用地图实现的解决方案更难理解,但可以肯定 Manu 是对的,对于大型收藏,我不会使用这种直观的方式,而是选择上面的地图。
  • 这是这里为数不多的正确答案之一。大多数其他的实际上并没有反转流,他们试图以某种方式避免这样做(这只在你通常不需要首先反转的特殊情况下才有效)。如果您尝试反转不适合内存的流,那么无论如何您都做错了。将其转储到数据库并使用常规 SQL 获取反向流。
【解决方案4】:

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

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

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

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

【讨论】:

  • 很棒又简单。这是来自一些开源实用程序吗? (Estreams 的事情)还是你的一些代码?
  • 谢谢!不幸的是,我不打算泄露Estreams 的名字(我打算从帖子中删除它)。它是我们公司的内部实用程序类之一,我们用它来补充java.util.stream.Streamstatic 方法。
  • 好吧……“棒极了而且简单”……这个解决方案是否可以处理任何情况,Stuart Marks’ even simpler solution 一年半前还没有处理?
  • 我刚刚用我上面的测试方法测试了他的解决方案;它通过了。我不必要地避免溢出,而不是像他那样拥抱它。我同意他的更好。我会编辑我的以反映这一点。
  • @vach,你可以使用StreamEx指定步骤:IntStreamEx.rangeClosed(from-1, to, -1)
【解决方案5】:

没有外部库...

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

}

【讨论】:

    【解决方案6】:

    如果实现Comparable&lt;T&gt;(例如IntegerStringDate),您可以使用Comparator.reverseOrder() 来实现。

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

    【讨论】:

    • 这不会反转流。它以相反的顺序对流进行排序。所以如果你有Stream.of(1,3,2),结果将是Stream.of(3,2,1)而不是Stream.of(2,3,1)
    【解决方案7】:

    如何不这样做:

    • 不要使用.sorted(Comparator.reverseOrder()).sorted(Collections.reverseOrder()),因为它只会按降序对元素进行排序。
      将其用于给定的整数输入:
      [1, 4, 2, 5, 3]
      输出如下:
      [5, 4, 3, 2, 1]
      对于字符串输入:
      ["A", "D", "B", "E", "C"]
      输出如下:
      [E, D, C, B, A]
    • 不要使用.sorted((a, b) -&gt; -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) -&gt; -1)
    它打破了比较器合同,可能仅适用于某些情况,即。仅在单线程上,但不并行。
    洋基解释:

    (a, b) -&gt; -1 违反了Comparator 的合同。这是否有效取决于排序算法的实现。 JVM 的下一个版本可能会打破这一点。实际上,我已经可以使用IntStream.range(0, 10000).parallel().boxed().sorted((a, b) -&gt; -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]

    【讨论】:

    • (a, b) -&gt; -1 违反了Comparator 的合同。这是否有效取决于排序算法的实现。 JVM 的下一个版本可能会打破这一点。实际上我已经可以在我的机器上使用IntStream.range(0, 10000).parallel().boxed().sorted((a, b) -&gt; -1).forEachOrdered(System.out::println);
    • @yankee 是的,你是对的。它违反了Comparator 合同,是对Comparator 的一种滥用。它适用于单线程,但即使你不使用.parallel(),你也不能依赖它,因为你不知道虚拟机将如何运行它,也不知道将使用哪种排序算法(甚至可能在单线程上工作的排序算法也可能会因这个实现而中断)。感谢您的评论。我会在空闲时间修改我的答案。
    • 我根据洋基评论修改了我的答案。现在答案应该是正确的。
    【解决方案8】:

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

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

    并像这样使用它:

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

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

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

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

    【讨论】:

      【解决方案9】:

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

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

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

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

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

      【讨论】:

      • 很高兴不知道这个项目
      • cyclops 现在还附带Spliterators 以实现高效的流反转(目前用于范围、数组和列表)。使用 SequenceM.of、SequenceM.range 或 SequenceM.fromList 创建 cyclops SequenceM 流扩展将自动利用高效的可逆拆分器。
      【解决方案10】:

      我建议使用jOOλ,它是一个很棒的库,它为 Java 8 流和 lambdas 添加了许多有用的功能。

      然后您可以执行以下操作:

      List<Integer> list = Arrays.asList(1,2,3,4);    
      Seq.seq(list).reverse().forEach(System.out::println)
      

      就这么简单。这是一个非常轻量级的库,非常值得添加到任何 Java 8 项目中。

      【讨论】:

        【解决方案11】:

        这是我想出的解决方案:

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

        然后使用这些比较器:

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

        【讨论】:

        • 这只是对您的“特定问题”的回答,而不是对您的“一般问题”的回答。
        • Collections.reverseOrder() 自 Java 1.2 以来就存在并与 Integer 一起使用...
        【解决方案12】:

        这个实用方法怎么样?

        public static <T> Stream<T> getReverseStream(List<T> list) {
            final ListIterator<T> listIt = list.listIterator(list.size());
            final Iterator<T> reverseIterator = new Iterator<T>() {
                @Override
                public boolean hasNext() {
                    return listIt.hasPrevious();
                }
        
                @Override
                public T next() {
                    return listIt.previous();
                }
            };
            return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                    reverseIterator,
                    Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
        }
        

        似乎适用于所有情况而不会重复。

        【讨论】:

        • 我非常喜欢这个解决方案。大多数答案分为两类:(1)反转集合并 .stream() 它,(2)吸引自定义收集器。两者都是绝对不必要的。否则,它会证明 JDK 8 本身存在一些严重的语言表达问题。而你的答案恰恰相反:)
        【解决方案13】:

        最简单的方式(简单收集 - 支持并行流):

        public static <T> Stream<T> reverse(Stream<T> stream) {
            return stream
                    .collect(Collector.of(
                            () -> new ArrayDeque<T>(),
                            ArrayDeque::addFirst,
                            (q1, q2) -> { q2.addAll(q1); return q2; })
                    )
                    .stream();
        }
        

        高级方式(持续支持并行流):

        public static <T> Stream<T> reverse(Stream<T> stream) {
            Objects.requireNonNull(stream, "stream");
        
            class ReverseSpliterator implements Spliterator<T> {
                private Spliterator<T> spliterator;
                private final Deque<T> deque = new ArrayDeque<>();
        
                private ReverseSpliterator(Spliterator<T> spliterator) {
                    this.spliterator = spliterator;
                }
        
                @Override
                @SuppressWarnings({"StatementWithEmptyBody"})
                public boolean tryAdvance(Consumer<? super T> action) {
                    while(spliterator.tryAdvance(deque::addFirst));
                    if(!deque.isEmpty()) {
                        action.accept(deque.remove());
                        return true;
                    }
                    return false;
                }
        
                @Override
                public Spliterator<T> trySplit() {
                    // After traveling started the spliterator don't contain elements!
                    Spliterator<T> prev = spliterator.trySplit();
                    if(prev == null) {
                        return null;
                    }
        
                    Spliterator<T> me = spliterator;
                    spliterator = prev;
                    return new ReverseSpliterator(me);
                }
        
                @Override
                public long estimateSize() {
                    return spliterator.estimateSize();
                }
        
                @Override
                public int characteristics() {
                    return spliterator.characteristics();
                }
        
                @Override
                public Comparator<? super T> getComparator() {
                    Comparator<? super T> comparator = spliterator.getComparator();
                    return (comparator != null) ? comparator.reversed() : null;
                }
        
                @Override
                public void forEachRemaining(Consumer<? super T> action) {
                    // Ensure that tryAdvance is called at least once
                    if(!deque.isEmpty() || tryAdvance(action)) {
                        deque.forEach(action);
                    }
                }
            }
        
            return StreamSupport.stream(new ReverseSpliterator(stream.spliterator()), stream.isParallel());
        }
        

        请注意,您可以快速扩展到其他类型的流(IntStream,...)。

        测试:

        // Use parallel if you wish only
        revert(Stream.of("One", "Two", "Three", "Four", "Five", "Six").parallel())
            .forEachOrdered(System.out::println);
        

        结果:

        Six
        Five
        Four
        Three
        Two
        One
        

        附加说明:simplest way 在与其他流操作一起使用时不是很有用(收集连接破坏了并行性)。 advance way 没有这个问题,它还保留了流的初始特征,例如SORTED,因此,它是在反向之后与其他流操作一起使用的方式。

        【讨论】:

          【解决方案14】:
          List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
                  newStream.forEach(System.out::println);
          

          【讨论】:

            【解决方案15】:

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

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

            【讨论】:

              【解决方案16】:

              作为参考,我正在查看相同的问题,我想以相反的顺序加入流元素的字符串值。

              itemList = { last, middle, first } => first,middle,last

              我开始使用来自comonadcollectingAndThenStuart MarksArrayDeque 收集器的中间集合,虽然我对中间集合不满意,并再次流式传输

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

              所以我迭代了使用 Collector.of 工厂的 Stuart Marks 答案,它具有有趣的 finisher lambda。

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

              由于在这种情况下流不是并行的,因此组合器的相关性并不高,为了代码的一致性,无论如何我都使用insert,但这并不重要,因为它取决于首先构建哪个字符串生成器.

              我查看了 StringJoiner,但它没有 insert 方法。

              【讨论】:

                【解决方案17】:

                不是纯粹的Java8,但如果你结合使用guava的Lists.reverse()方法,你可以轻松实现:

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

                【讨论】:

                  【解决方案18】:

                  ArrayDeque 在堆栈中比堆栈或链接列表更快。 "push()" 在 Deque 的前面插入元素

                   protected <T> Stream<T> reverse(Stream<T> stream) {
                      ArrayDeque<T> stack = new ArrayDeque<>();
                      stream.forEach(stack::push);
                      return stack.stream();
                  }
                  

                  【讨论】:

                    【解决方案19】:

                    反转字符串或任何数组

                    (Stream.of("abcdefghijklm 1234567".split("")).collect(Collectors.collectingAndThen(Collectors.toList(),list -> {Collections.reverse(list);return list;}))).stream().forEach(System.out::println);
                    

                    可以根据分隔符或空格修改分割

                    【讨论】:

                      【解决方案20】:

                      关于生成反向IntStream的具体问题:

                      Java 9 开始,您可以使用 IntStream.iterate(...) 的三参数版本:

                      IntStream.iterate(10, x -> x >= 0, x -> x - 1).forEach(System.out::println);
                      
                      // Out: 10 9 8 7 6 5 4 3 2 1 0
                      

                      地点:

                      IntStream.iterate​(int seed, IntPredicate hasNext, IntUnaryOperator next);

                      • seed - 初始元素;
                      • hasNext - 应用于元素的谓词以确定何时 流必须终止;
                      • next - 应用于前一个元素的函数以生成 新元素。

                      【讨论】:

                        【解决方案21】:
                        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
                            });
                        }
                        

                        【讨论】:

                          【解决方案22】:

                          回答使用 IntStream 反转的具体问题,以下对我有用:

                          IntStream.range(0, 10)
                            .map(x -> x * -1)
                            .sorted()
                            .map(Math::abs)
                            .forEach(System.out::println);
                          

                          【讨论】:

                            【解决方案23】:

                            最简单的解决方案是使用List::listIteratorStream::generate

                            List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
                            ListIterator<Integer> listIterator = list.listIterator(list.size());
                            
                            Stream.generate(listIterator::previous)
                                  .limit(list.size())
                                  .forEach(System.out::println);
                            

                            【讨论】:

                            • 值得补充的是Stream.generate()是无限流生成的,所以这里对limit()的调用非常重要。
                            【解决方案24】:

                            此方法适用于任何 Stream 并且符合 Java 8:

                            Stream<Integer> myStream = Stream.of(1, 2, 3, 4, 5);
                            myStream.reduce(Stream.empty(),
                                    (Stream<Integer> a, Integer b) -> Stream.concat(Stream.of(b), a),
                                    (a, b) -> Stream.concat(b, a))
                                    .forEach(System.out::println);
                            

                            【讨论】:

                              【解决方案25】:

                              我就是这样做的。

                              我不喜欢创建一个新集合并反向迭代它的想法。

                              IntStream#map 的想法非常简洁,但我更喜欢 IntStream#iterate 方法,因为我认为倒计时到零的想法可以用 iterate 方法更好地表达,并且在将数组从后面遍历到前面。

                              import static java.lang.Math.max;
                              
                              private static final double EXACT_MATCH = 0d;
                              
                              public static IntStream reverseStream(final int[] array) {
                                  return countdownFrom(array.length - 1).map(index -> array[index]);
                              }
                              
                              public static DoubleStream reverseStream(final double[] array) {
                                  return countdownFrom(array.length - 1).mapToDouble(index -> array[index]);
                              }
                              
                              public static <T> Stream<T> reverseStream(final T[] array) {
                                  return countdownFrom(array.length - 1).mapToObj(index -> array[index]);
                              }
                              
                              public static IntStream countdownFrom(final int top) {
                                  return IntStream.iterate(top, t -> t - 1).limit(max(0, (long) top + 1));
                              }
                              

                              这里有一些测试来证明它有效:

                              import static java.lang.Integer.MAX_VALUE;
                              import static org.junit.Assert.*;
                              
                              @Test
                              public void testReverseStream_emptyArrayCreatesEmptyStream() {
                                  Assert.assertEquals(0, reverseStream(new double[0]).count());
                              }
                              
                              @Test
                              public void testReverseStream_singleElementCreatesSingleElementStream() {
                                  Assert.assertEquals(1, reverseStream(new double[1]).count());
                                  final double[] singleElementArray = new double[] { 123.4 };
                                  assertArrayEquals(singleElementArray, reverseStream(singleElementArray).toArray(), EXACT_MATCH);
                              }
                              
                              @Test
                              public void testReverseStream_multipleElementsAreStreamedInReversedOrder() {
                                  final double[] arr = new double[] { 1d, 2d, 3d };
                                  final double[] revArr = new double[] { 3d, 2d, 1d };
                                  Assert.assertEquals(arr.length, reverseStream(arr).count());
                                  Assert.assertArrayEquals(revArr, reverseStream(arr).toArray(), EXACT_MATCH);
                              }
                              
                              @Test
                              public void testCountdownFrom_returnsAllElementsFromTopToZeroInReverseOrder() {
                                  assertArrayEquals(new int[] { 4, 3, 2, 1, 0 }, countdownFrom(4).toArray());
                              }
                              
                              @Test
                              public void testCountdownFrom_countingDownStartingWithZeroOutputsTheNumberZero() {
                                  assertArrayEquals(new int[] { 0 }, countdownFrom(0).toArray());
                              }
                              
                              @Test
                              public void testCountdownFrom_doesNotChokeOnIntegerMaxValue() {
                                  assertEquals(true, countdownFrom(MAX_VALUE).anyMatch(x -> x == MAX_VALUE));
                              }
                              
                              @Test
                              public void testCountdownFrom_givesZeroLengthCountForNegativeValues() {
                                  assertArrayEquals(new int[0], countdownFrom(-1).toArray());
                                  assertArrayEquals(new int[0], countdownFrom(-4).toArray());
                              }
                              

                              【讨论】:

                                【解决方案26】:

                                在这一切中,我看不到我会首先找到的答案。

                                这并不是问题的直接答案,但它是问题的潜在解决方案。

                                首先将列表向后构建。如果可以,请使用 LinkedList 而不是 ArrayList,并且在添加项目时使用“Push”而不是 add。该列表将以相反的顺序构建,然后无需任何操作即可正确流式传输。

                                这不适用于处理原始数组或列表的情况,这些数组或列表已经以各种方式使用,但在数量惊人的情况下效果很好。

                                【讨论】:

                                  【解决方案27】:

                                  基于@stuart-marks's answer,但没有强制转换,函数返回从end开始的列表元素流:

                                  public static <T> Stream<T> reversedStream(List<T> tList) {
                                      final int size = tList.size();
                                      return IntStream.range(0, size)
                                              .mapToObj(i -> tList.get(size - 1 - i));
                                  }
                                  
                                  // usage
                                  reversedStream(list).forEach(System.out::println);
                                  

                                  【讨论】:

                                    【解决方案28】:

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

                                    public static <T> void reverseHelper(List<T> li){
                                    
                                     li.stream()
                                    .sorted((x,y)-> -1)
                                    .collect(Collectors.toList())
                                    .forEach(System.out::println);
                                    
                                        }
                                    

                                    【讨论】:

                                    • 你违反了Comparator的合同。因此,没有人可以保证这个“技巧”在任何未来的 Java 版本中都可以使用任何排序算法。同样的技巧不适用于并行流,例如,并行排序算法以不同的方式使用Comparator。对于顺序排序,它纯粹是偶然的。我不建议任何人使用此解决方案。
                                    • 另外设置System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");时也不起作用
                                    • 我认为它只是以相反的顺序而不是按排序的顺序(即降序)打印东西,它也适用于并行流:public static &lt;T&gt; void reverseHelper(List&lt;T&gt; li){ li.parallelStream() .sorted((x,y)-&gt;-1) .collect(Collectors.toList()) .forEach(System.out::println); }
                                    • 试试reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(结果可能取决于核心数量)。
                                    【解决方案29】:

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

                                    【讨论】:

                                    • 这是反向排序,而不是还原列表。
                                    猜你喜欢
                                    • 2022-01-18
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2015-07-07
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多