将两个Java 8流或一个额外元素添加到流中

Mar*_*rcG 162 java concat java-8 java-stream

我可以添加流或额外的元素,如下所示:

Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));
Run Code Online (Sandbox Code Playgroud)

我可以随时添加新内容,如下所示:

Stream stream = Stream.concat(
                       Stream.concat(
                              stream1.filter(x -> x!=0), stream2)
                              .filter(x -> x!=1),
                                  Stream.of(element))
                                  .filter(x -> x!=2);
Run Code Online (Sandbox Code Playgroud)

但这很难看,因为它concat是静态的.如果concat是实例方法,上面的示例将更容易阅读:

 Stream stream = stream1.concat(stream2).concat(element);
Run Code Online (Sandbox Code Playgroud)

 Stream stream = stream1
                 .filter(x -> x!=0)
                 .concat(stream2)
                 .filter(x -> x!=1)
                 .concat(element)
                 .filter(x -> x!=2);
Run Code Online (Sandbox Code Playgroud)

我的问题是:

1)concat静态有什么好的理由吗?或者是否有一些我缺少的等效实例方法?

2)无论如何,有没有更好的方法呢?

Edw*_*rzo 160

不幸的是,这个答案可能很少或没有任何帮助,但我对Java Lambda邮件列表进行了取证分析,看看我是否能找到这种设计的原因.这就是我发现的.

最初有一个Stream.concat(Stream)的实例方法

在邮件列表中,我可以清楚地看到该方法最初是作为实例方法实现的,正如您可以阅读Paul Sandoz 在此主题中关于concat操作的内容.

他们在其中讨论了流可能无限的情况以及在这些情况下串联意味着什么的问题,但我不认为这是修改的原因.

您在另一个线程中看到JDK 8的一些早期用户在使用null参数时询问了concat实例方法的行为.

这个其他线程显示,虽然,concat的设计是正在讨论中.

重构为Streams.concat(Stream,Stream)

但是没有任何解释,突然之间,方法被改为静态方法,正如你在这个线程中看到的关于组合流的方法.这可能是唯一能够揭示这一变化的邮件主题,但我不清楚确定重构的原因.但我们可以看到他们做了一个提交,他们建议将concat方法移出Stream辅助类Streams.

重构为Stream.concat(Stream,Stream)

后来,它被再次移动StreamsStream,但再次,对于没有任何解释.

所以,底线,设计的原因对我来说并不完全清楚,我找不到一个好的解释.我猜你仍然可以在邮件列表中提问.

流连接的一些替代方案

其他线程由迈克尔·希克森讨论/询问等方式来结合/ CONCAT流

  1. 要组合两个流,我应该这样做:

    Stream.concat(s1, s2)
    
    Run Code Online (Sandbox Code Playgroud)

    不是这个:

    Stream.of(s1, s2).flatMap(x -> x)
    
    Run Code Online (Sandbox Code Playgroud)

    ... 对?

  2. 要合并两个以上的流,我应该这样做:

    Stream.of(s1, s2, s3, ...).flatMap(x -> x)
    
    Run Code Online (Sandbox Code Playgroud)

    不是这个:

    Stream.of(s1, s2, s3, ...).reduce(Stream.empty(), Stream::concat)
    
    Run Code Online (Sandbox Code Playgroud)

    ... 对?

  • +1好的研究.我将使用它作为我的Stream.concat采用varargs:`public static <T> Stream <T> concat(Stream <T> ... streams){return Stream.of(streams).reduce(Stream.empty( ),Stream :: concat);}` (6认同)

nos*_*sid 124

如果为Stream.concatStream.of添加静态导入,则第一个示例可以编写如下:

Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));
Run Code Online (Sandbox Code Playgroud)

使用通用名称导入静态方法可能会导致代码难以阅读和维护(命名空间污染).因此,使用更有意义的名称创建自己的静态方法可能更好.但是,为了演示,我会坚持使用这个名字.

public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
    return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
    return Stream.concat(lhs, Stream.of(rhs));
}
Run Code Online (Sandbox Code Playgroud)

使用这两种静态方法(可选地与静态导入结合使用),这两个示例可以编写如下:

Stream<Foo> stream = concat(stream1, concat(stream2, element));

Stream<Foo> stream = concat(
                         concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
                         element)
                     .filter(x -> x!=2);
Run Code Online (Sandbox Code Playgroud)

现在代码明显缩短了.但是,我同意可读性没有改善.所以我有另一个解决方案.


在很多情况下,收集器可用于扩展流的功能.底部有两个收集器,这两个例子可以写成如下:

Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));

Stream<Foo> stream = stream1
                     .filter(x -> x!=0)
                     .collect(concat(stream2))
                     .filter(x -> x!=1)
                     .collect(concat(element))
                     .filter(x -> x!=2);
Run Code Online (Sandbox Code Playgroud)

您需要的语法和上面的语法之间的唯一区别是,您必须使用collect(concat(...))替换concat(... ).这两个静态方法可以如下实现(可选择与静态导入结合使用):

private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
    return Collector.of(
        collector.supplier(),
        collector.accumulator(),
        collector.combiner(),
        collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
    return combine(Collectors.toList(),
        list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
    return concat(Stream.of(element));
}
Run Code Online (Sandbox Code Playgroud)

当然,应该提到这种解决方案的缺点.collect是一个消耗流的所有元素的最终操作.最重要的是,每次在链中使用时,collector concat都会创建一个中间ArrayList.这两种操作都会对程序的行为产生重大影响.但是,如果可读性性能更重要,它可能仍然是一种非常有用的方法.

  • 当我不需要太多表现时,我将使用它. (3认同)

Tag*_*eev 12

我的StreamEx库扩展了Stream API的功能.特别是它提供了诸如appendprepend之类的方法来解决这个问题(他们在内部使用concat).这些方法可以接受另一个流或集合或varargs数组.使用我的库你的问题可以通过这种方式解决(请注意,x != 0对于非原始流看起来很奇怪):

Stream<Integer> stream = StreamEx.of(stream1)
             .filter(x -> !x.equals(0))
             .append(stream2)
             .filter(x -> !x.equals(1))
             .append(element)
             .filter(x -> !x.equals(2));
Run Code Online (Sandbox Code Playgroud)

顺便说一下,还有一个filter操作的快捷方式:

Stream<Integer> stream = StreamEx.of(stream1).without(0)
                                 .append(stream2).without(1)
                                 .append(element).without(2);
Run Code Online (Sandbox Code Playgroud)


her*_*man 9

做就是了:

Stream.of(stream1, stream2, Stream.of(element)).flatMap(identity());
Run Code Online (Sandbox Code Playgroud)

哪里identity()是静态导入Function.identity().

将多个流连接成一个流与展平流相同.

但是,遗憾的是,由于某些原因,没有flatten()方法Stream,所以你必须使用flatMap()身份功能.