为什么Java Streams没有中间组合操作?

neX*_*Xus 2 java-8 java-stream

注意:我不一定在寻找下面描述的具体示例问题的解决方案.我真的很感兴趣为什么在Java 8中不可能开箱即用.


Java流是懒惰的.最后他们有一个终端操作.
我的解释是这个终端操作将通过流提取所有值.没有任何中间操作可以做到这一点.为什么没有中间操作通过流引入任意数量的元素?像这样的东西:

stream
    .mapMultiple(this::consumeMultipleElements) // or groupAndMap or combine or intermediateCollect or reverseFlatMap
    .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

当下游操作尝试使流一次前进时,中间操作可能尝试多次上行(或根本不上行).

我会看到几个用例:(
这些只是示例.所以你可以看到它确实可以处理这些用例,但它不是"流式传输方式",而且这些解决方案缺乏Streams所具有的理想的懒惰属性. )

  • 将多个元素组合到一个新元素中,以传递到流的其余部分.(例如,成对(1,2,3,4,5,6) ? ((1,2),(3,4),(5,6)))

    // Something like this,
    // without needing to consume the entire stream upfront,
    // and also more generic. (The combiner should decide for itself how many elements to consume/combine per resulting element. Maybe the combiner is a Consumer<Iterator<E>> or a Consumer<Supplier<E>>)
    public <E, R> Stream<R> combine(Stream<E> stream, BiFunction<E, E, R> combiner) {
        List<E> completeList = stream.collect(toList());
        return IntStream.range(0, completeList.size() / 2)
            .mapToObj(i -> combiner.apply(
                    completeList.get(2 * i),
                    completeList.get(2 * i + 1)));
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 确定Stream是否为空(将Stream映射到可选的非空流)

    // Something like this, without needing to consume the entire stream
    public <E> Optional<Stream<E>> toNonEmptyStream(Stream<E> stream) {
        List<E> elements = stream.collect(toList());
        return elements.isEmpty()
            ? Optional.empty()
            : Optional.of(elements.stream());
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 拥有一个Iterator不会终止流的惰性(允许基于更复杂的逻辑跳过元素skip(long n)).

    Iterator<E> iterator = stream.iterator();
    // Allow this without throwing a "java.lang.IllegalStateException: stream has already been operated upon or closed"
    stream.collect(toList());
    
    Run Code Online (Sandbox Code Playgroud)

当他们设计Streams及其周围的一切时,他们是否忘记了这些用例,还是明确地将其删除了?
我知道在处理并行流时这些可能会产生意想不到的结果,但在我看来,这是一个可以记录的风险.

Eug*_*ene 5

那么你想要的所有操作实际上都可以实现Stream API,但不是开箱即用的.

将多个元素组合成元素对 - 您需要一个自定义Spliterator.这是Tagir Valeev这样做的.他有一个绝对的图书馆野兽叫做StreamEx许多其他有用的东西,不支持开箱即用.

我不明白你的第二个例子,但我敢打赌它也是可行的.

skip一个更复杂的操作是在java-9通过dropWhiletakeWhile该采取Predicate作为输入.

请注意,当你说没有任何中间操作可以做到这一点时,这是不准确的 - 确实存在sorted并且distinct确实如此.否则他们无法工作.也有flatMap这样的行为,但这更像是一个错误.

还有一件事是并行流的中间操作没有定义的顺序,因此这种有状态的中间操作将具有并行流的未知条目.另一方面,您总是可以选择滥用以下内容:

List<Something> list = Collections.synchronizedList()
.map(x -> {
     list.add(x);
     // your mapping
 })
Run Code Online (Sandbox Code Playgroud)

如果我是你并且真的认为我可能需要那个,我不会这样做,但为了以防万一......

  • 在这种情况下我不会提到`flatMap`,它比澄清更令人困惑.没有它,答案很有效. (4认同)