Java 8流逆序

 濮阳土著_480 发布于 2023-01-12 00:12

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

具体问题:

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

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

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

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

我在这里想念的是什么?

10 个回答
  • 对于生成反向的具体问题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-01-29

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

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

    完整的代码是这样的:

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

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

    2023-01-12 00:19 回答
  • 这里的许多解决方案排序或反转IntStream,但这不必要地需要中间存储.Stuart Marks的解决方案是要走的路:

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

    2023-01-12 00:23 回答
  • 优雅的解决方

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

    2023-01-12 00:25 回答
  • 没有外部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;
            });
        }
    
    }
    

    2023-01-12 00:27 回答
  • 一般问题:

    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)
    

    2023-01-12 00:29 回答
  • 如果实施Comparable<T>(例如,Integer,String,Date),你可以用做Comparator.reverseOrder().

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

    2023-01-12 00:33 回答
  • 您可以定义自己的收集器,以相反的顺序收集元素:

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

    2023-01-12 00:39 回答
  • 我建议使用jOO?,它是一个很棒的库,为Java 8流和lambda添加了许多有用的功能。

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

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

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

    2023-01-12 00:40 回答
  • 这是我提出的解决方案:

    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...
    

    2023-01-12 00:41 回答
  • cyclops -react StreamUtils有一个反向流方法(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);
    

    2023-01-12 00:47 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有